版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.10.09 星期五 |
前言
在我们开发中总有从远程下载图片,OC中有个很成熟的三方框架,大家都知道的
SDWebImage
,同样Swift中也有类似的三方框架Nuke
,接下来几篇我们就一起看一下这个框架。
开始
首先我们看一下该项目的GitHub地址 Nuke。
Nuke提供了一种简单有效的方法来下载和显示应用程序中的图像。 简洁明了的API背后是先进的体系结构,该体系结构具有其独特的功能并提供了几乎无限的自定义可能性。 Nuke的主要功能是性能 - performance。
Fast LRU memory and disk cache · SwiftUI · Smart background decompression · Image processing · Elegant builder API · Resumable downloads · Intelligent deduplication · Request prioritization · Low data mode · Prefetching · Rate limiting · Progressive JPEG, HEIF, WebP, SVG, GIF · Alamofire · Combine · Reactive extensions
Nuke
易于学习和使用。 以下是其API和功能的概述:
- Image View Extensions - UI Extensions · Table View · Placeholders, Transitions ·
ImageRequest
- Image Processing -
Resize
·Circle
·RoundedCorners
·GaussianBlur
·CoreImage
· Custom Processors - Image Pipeline - Load Image ·
ImageTask
· Customize Image Pipeline · Default Pipeline - Caching - LRU Memory Cache · HTTP Disk Cache · Aggressive LRU Disk Cache
- Advanced Features - Preheat Images · Progressive Decoding
- Extensions ‣ FetchImage · Builder · Combine · RxNuke · And More
要了解更多信息,请参阅完整的API Reference,并查看存储库中包含的演示项目。 准备安装时,请遵循Installation Guide。 请参阅Requirements,以获取受支持平台的列表。 如果遇到任何问题,请参阅FAQ或Troubleshooting Guide。
要了解有关管道和支持的格式的更多信息,请参见专用指南。
- Image Formats ‣ Progressive JPEG · HEIF · GIF · SVG · WebP
- Image Pipeline ‣ Data Loading · Resumable Downloads · Memory Cache · Deduplication · Decompression · Performance
Image View Extensions
用单行代码下载并在image view
中显示图像:
Nuke.loadImage(with: url, into: imageView)
Nuke
将检查图像是否在内存缓存中,如果存在,将立即显示它。 否则,图像数据将在后台加载,解码,处理和解压缩
请参阅 Image Pipeline Guide以了解如何下载和处理图像。
1. In a Table View
当您为现有视图请求新图像时,Nuke
会为重用做好准备,并取消对该视图的所有未完成请求。
func tableView(_ tableView: UITableView, cellForItemAt indexPath: IndexPaths) -> UITableViewCell {
/* Create a cell ... */
Nuke.loadImage(with: url, into: cell.imageView)
}
视图
deallocated
后,关联的请求将自动取消。 要手动取消请求,请调用Nuke.cancelRequest(for:imageView)
。
2. Placeholders, Transitions, Content Modes, Tint Colors
使用ImageLoadingOptions
设置一个placeholder
,选择一个内置transitions
,或提供一个自定义过渡。
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
transition: .fadeIn(duration: 0.33)
)
Nuke.loadImage(with: url, options: options, into: imageView)
您甚至可以自定义content mode
或每种图像类型的tint color
:
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failureImage"),
contentModes: .init(success: .scaleAspectFill, failure: .center, placeholder: .center),
tintColors: .init(success: .green, failure: .red, placeholder: .yellow)
)
如果希望所有图像视图都具有相同的行为,则可以修改
ImageLoadingOptions.shared
。
请记住,图像视图的内置扩展程序旨在使您尽快启动并运行。 如果您想拥有更多控制权或使用某些高级功能(例如动画图像),建议直接在自定义视图中使用ImagePipeline
。
3. ImageRequest
ImageRequest
允许您设置图像处理器,更改请求优先级等:
let request = ImageRequest(
url: URL(string: "http://..."),
processors: [ImageProcessors.Resize(size: imageView.bounds.size)],
priority: .high
)
应用处理器的另一种方法是在
ImagePipeline.Configuration
上设置默认processors
。 这些处理器将应用于管道加载的所有图像。 如果请求中包含非空的处理器数组,则将应用它们。
可通过ImageRequestOptions
使用的高级选项。 例如,如果URL
包含瞬态查询参数,则可以提供一个filteredURL
用作缓存的键。
let request = ImageRequest(
url: URL(string: "http://example.com/image.jpeg?token=123")!,
options: ImageRequestOptions(
filteredURL: "http://example.com/image.jpeg"
)
)
有更多选项可用,要查看所有选项,请查看内联文档以了解ImageRequestOptions
。
Image Processing
Nuke
具有强大而高效的图像处理基础架构,具有多个内置处理器,您可以在ImageProcessors
命名空间中找到它们,例如 ImageProcessors.Resize
。
此截图和其他屏幕截图来自仓库中包含的演示项目。
1. Resize
要调整图像大小,请使用ImageProcessors.Resize
:
ImageRequest(url: url, processors: [
ImageProcessors.Resize(size: imageView.bounds.size)
])
默认情况下,target size
以点为单位。 加载图像后,Nuke
将缩小图像以填充目标区域,并保持宽高比。 要裁切图像,请将crop
设置为true
。 有关更多选项,请参见ImageProcessors.Resize
文档。
使用可选的Builder软件包获得更简洁的API。
pipeline.image(with: URL(string: "https://")!) .resize(width: 320) .blur(radius: 10)
2. Circle
将图像的角圆成带有可选边框的圆。
ImageRequest(url: url, processors: [
ImageProcessors.Circle()
])
3. RoundedCorners
将图像的角圆整到指定的半径。 确保调整图像大小以与显示图像的视图大小完全匹配,以便正确显示边框。
ImageRequest(url: url, processors: [
ImageProcessors.Circle(radius: 16)
])
4. GaussianBlur
ImageProcessors.GaussianBlur
使用Core Image滤镜之一模糊输入图像。
5. CoreImageFilter
使用ImageProcessors.CoreImageFilter
应用大量的Core Image filters:
ImageProcessors.CoreImageFilter(name: "CISepiaTone")
6. Custom Processors
对于简单的一次性操作,请使用ImageProcessors.Anonymous
创建带闭包的处理器。
定制处理器需要实现ImageProcessing
协议。 为了满足基本的图像处理需求,请实现process(_ :)
方法并创建一个唯一标识处理器的标识符。 对于没有输入参数的处理器,您可以返回静态字符串。
public protocol ImageProcessing {
func process(image: UIImage) -> UIImage? // NSImage on macOS
var identifier: String // get
}
如果您的处理器需要操纵图像元数据(ImageContainer)
,或者需要通过ImageProcessingContext
访问更多信息,那么除了process(_ :)
之外,您还可以实现其他方法。
public protocol ImageProcessing {
func process(_ image container: ImageContainer, context: ImageProcessingContext) -> ImageContainer?
}
除了var identfier:String
外,您还可以实现var hashableIdentifier:AnyHashable
,以供内存高速缓存使用,因为在这种情况下,字符串操作太慢。 默认情况下,此方法返回identifier
字符串。 一种常见的方法是使处理器可Hashable
,并从hashableIdentifier
返回self
。
Image Pipeline
Nuke
的核心是ImagePipeline
类。 直接使用管道来加载图像而不显示它们:
let task = ImagePipeline.shared.loadImage(
with: url,
progress: { _, completed, total in
print("progress updated")
},
completion: { result: Result in
print("task completed")
}
)
loadImage
返回值始终异步调用完成闭包。 若要检查图像是否存储在内存缓存中,请使用pipeline.cachedImage(for:url)
。
要下载数据而不进行任何昂贵的解码或处理,请使用
loadData(with:progress:completion :)
。
1. ImageTask
启动请求时,管道将返回ImageTask
对象,该对象可用于取消操作以及更多操作。
task.cancel()
task.priority = .high
2. Customize Image Pipeline
如果您想构建一个适合您特定需求的系统,那么您将不会失望。 有很多事情需要调整。 您可以设置自定义数据加载器和缓存,配置图像编码器和解码器,更改每个单独阶段的并发操作数,禁用和启用重复数据删除和速率限制等功能。
要了解更多信息,请参见
ImagePipeline.Configuration
和Image Pipeline Guide的内联文档。
以下是可用于自定义的协议:
-
DataLoading
–下载(或返回缓存的)图像数据 -
DataCaching
–将图像数据存储在磁盘上 -
ImageDecoding
–将数据转换为图像(有关新的实验性解码功能,请参见_ImageDecoding
) -
ImageEncoding
-将图像转换为数据 -
ImageProcessing
–应用图像转换 -
ImageCaching
–将图像存储到内存缓存中
整个配置由ImagePipeline.Configuration
结构描述。 要使用自定义配置创建管道,请调用ImagePipeline(configuration :)
初始值设定项或使用便捷的一种:
let pipeline = ImagePipeline {
$0.dataLoader = ...
$0.dataLoadingQueue = ...
$0.imageCache = ...
...
}
然后将新管道设置为默认值:
ImagePipeline.shared = pipeline
3. Default Image Pipeline
默认图像管道使用以下依赖项(dependencies)
进行初始化:
// Shared image cache with a size limit of ~20% of available RAM.
imageCache = ImageCache.shared
// Data loader with a default `URLSessionConfiguration` and a custom `URLCache`
// with memory capacity 0, and disk capacity 150 MB.
dataLoader = DataLoader()
// Custom aggressive disk cache is disabled by default.
dataCache = nil
// By default uses the decoder from the global registry and the default encoder.
makeImageDecoder = ImageDecoderRegistry.shared.decoder(for:)
makeImageEncoder = { _ in ImageEncoders.Default() }
管道中的每个操作都在专用队列上运行:
dataLoadingQueue.maxConcurrentOperationCount = 6
dataCachingQueue.maxConcurrentOperationCount = 2
imageDecodingQueue.maxConcurrentOperationCount = 1
imageEncodingQueue.maxConcurrentOperationCount = 1
imageProcessingQueue.maxConcurrentOperationCount = 2
imageDecompressingQueue.maxConcurrentOperationCount = 2
您可以调整以下管道设置列表:
// Automatically decompress images in the background by default.
isDecompressionEnabled = true
// Configure what content to store in the custom disk cache.
dataCacheOptions.storedItems = [.finalImage] // [.originalImageData]
// Avoid doing any duplicated work when loading or processing images.
isDeduplicationEnabled = true
// Rate limit the requests to prevent trashing of the subsystems.
isRateLimiterEnabled = true
// Progressive decoding is an opt-in feature because it is resource intensive.
isProgressiveDecodingEnabled = false
// Don't store progressive previews in memory cache.
isStoringPreviewsInMemoryCache = false
// If the data task is terminated (either because of a failure or a
// cancellation) and the image was partially loaded, the next load will
// resume where it was left off.
isResumableDataEnabled = true
在所有管道之间共享一些全局选项:
// Enable to start using `os_signpost` to monitor the pipeline
// performance using Instruments.
ImagePipeline.Configuration.isSignpostLoggingEnabled = false
Caching
1. LRU Memory Cache
Nuke
的默认ImagePipeline
具有两个缓存层。
首先,有一个内存缓存,用于存储准备显示的已处理图像。
// Configure cache
ImageCache.shared.costLimit = 1024 * 1024 * 100 // 100 MB
ImageCache.shared.countLimit = 100
ImageCache.shared.ttl = 120 // Invalidate image after 120 sec
// Read and write images
let request = ImageRequest(url: url)
ImageCache.shared[request] = ImageContainer(image: image)
let image = ImageCache.shared[request]
// Clear cache
ImageCache.shared.removeAll()
ImageCache
使用LRU算法-在扫描期间,首先删除最近最少使用的条目。
2. HTTP Disk Cache
未处理的图像数据存储在URLCache
中。
// Configure cache
DataLoader.sharedUrlCache.diskCapacity = 100
DataLoader.sharedUrlCache.memoryCapacity = 0
// Read and write responses
let request = ImageRequest(url: url)
let _ = DataLoader.sharedUrlCache.cachedResponse(for: request.urlRequest)
DataLoader.sharedUrlCache.removeCachedResponse(for: request.urlRequest)
// Clear cache
DataLoader.sharedUrlCache.removeAllCachedResponses()
3. Aggressive LRU Disk Cache
如果您不愿意使用HTTP
缓存,则可以尝试使用自定义LRU
磁盘缓存进行快速可靠的主动数据缓存(忽略 HTTP cache control)。 您可以使用管道配置启用它。
ImagePipeline {
$0.dataCache = try? DataCache(name: "com.myapp.datacache")
// Also consider disabling the native HTTP cache, see `DataLoader`.
}
默认情况下,管道仅存储原始图像数据。 要存储已下载和已处理的图像,请将dataCacheOptions.storedItems
设置为[.finalImage]
。 如果您要存储已处理的内容,例如下采样的图像,或者如果您想将图像转码为更有效的格式,例如HEIF
。
要节省磁盘空间,请参阅
HEIF
支持的ImageEncoders.ImageIO
和ImageEncoder.isHEIFPreferred
选项。
Advanced Features
1. Image Preheating
预先预取图像可以显着改善应用程序的用户体验。
// Make sure to keep a strong reference to preheater.
let preheater = ImagePreheater()
preheater.startPreheating(with: urls)
// Cancels all of the preheating tasks created for the given requests.
preheater.stopPreheating(with: urls)
要了解有关您可以执行的其他性能优化的更多信息,请参见Performance Guide。
请记住,预取会占用用户的数据,并给CPU和内存带来额外的压力。 为了减少CPU和内存的使用,您可以选择仅选择磁盘缓存作为预取目标:
// The preheater with `.diskCache` destination will skip image data decoding
// entirely to reduce CPU and memory usage. It will still load the image data
// and store it in disk caches to be used later.
let preheater = ImagePreheater(destination: .diskCache)
在iOS
上,您可以将prefetching APIs与ImagePreheater
结合使用以自动执行该过程。
2. Progressive Decoding
要启用渐进式图像解码,请将isProgressiveDecodingEnabled
配置选项设置为true
。
let pipeline = ImagePipeline {
$0.isProgressiveDecodingEnabled = true
// If `true`, the pipeline will store all of the progressively generated previews
// in the memory cache. All of the previews have `isPreview` flag set to `true`.
$0.isStoringPreviewsInMemoryCache = true
}
就是这样,管道将自动执行正确的操作,并在进度扫描到达时通过progress
闭包进行渐进式扫描:
let imageView = UIImageView()
let task = ImagePipeline.shared.loadImage(
with: url,
progress: { response, _, _ in
if let response = response {
imageView.image = response.image
}
},
completion: { result in
// Display the final image
}
)
Extensions
Nuke
有多种扩展:
Name | Description |
---|---|
FetchImage | SwiftUI integration |
ImagePublisher | Combine publishers for Nuke |
ImageTaskBuilder | A fun and convenient way to use Nuke |
Alamofire Plugin | Replace networking layer with Alamofire and combine the power of both frameworks |
RxNuke | RxSwift extensions for Nuke with examples of common use cases solved by Rx |
WebP Plugin | [Community] WebP support, built by Ryo Kosuge |
Gifu Plugin | Use Gifu to load and display animated GIFs |
FLAnimatedImage Plugin | Use FLAnimatedImage to load and display animated GIFs |
Xamarin NuGet | [Community] Makes it possible to use Nuke from Xamarin |
1. FetchImage
FetchImage
是一个Swift
软件包,可轻松使用Nuke
下载图像并将其显示在SwiftUI
应用中。 有关更多信息,请参见introductory post。
注意:这是一个
API
预览,将来可能会更改。
public struct ImageView: View {
@ObservedObject var image: FetchImage
public var body: some View {
ZStack {
Rectangle().fill(Color.gray)
image.view?
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}
2. Low Data Mode
FetchImage
还通过特殊的初始化程序为低数据模式提供内置支持:
FetchImage(regularUrl: highQualityUrl, lowDataUrl: lowQualityUrl)
3. Builder
觉得默认的API有点无聊吗? 试试ImageTaskBuilder,这是一种使用Nuke
的有趣且方便的方法。
ImagePipeline.shared.image(with: URL(string: "https://")!)
.fill(width: 320)
.blur(radius: 10)
.priority(.high)
.start { result in
print(result)
}
// Returns `ImageTask` when started.
let imageView: UIImageView
ImagePipeline.shared.image(with: URL(string: "https://")!)
.fill(width: imageView.size.width)
.display(in: imageView)
4. RxNuke
RxNuke为Nuke添加了RxSwift扩展,并启用了常见用例:Going from low to high resolution | Loading the first available image | Showing stale image while validating it | Load multiple images, display all at once | Auto retry on failures | And more
要了解使用此扩展程序可以做什么,请看一下先加载低分辨率图像然后切换到高分辨率有多么容易:
let pipeline = ImagePipeline.shared
Observable.concat(pipeline.loadImage(with: lowResUrl).orEmpty,
pipeline.loadImage(with: highResUrl).orEmpty)
.subscribe(onNext: { imageView.image = $0 })
.disposed(by: disposeBag)
5. Combine
ImagePublisher为Nuke
添加了Combine
发行者,并且与RxNuke一样,启用了各种功能强大的用例。
Contribution
Nuke's roadmap在Trello
中管理,并且可以公开获得。 如果您想贡献,请随时创建PR
。
1. Minimum Requirements
Nuke | Swift | Xcode | Platforms |
---|---|---|---|
Nuke 9.0 | Swift 5.1 | Xcode 11.0 | iOS 11.0 / watchOS 4.0 / macOS 10.13 / tvOS 11.0 |
Nuke 8.0 | Swift 5.0 | Xcode 10.2 | iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0 |
有关旧版本的信息,请参见Installation Guide。
后记
本篇主要讲述了Swift中也有类似的三方框架
Nuke
,感兴趣的给个赞或者关注~~~