版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.12.27 星期四 |
前言
OC对有一个很棒的网络图片加载控件SDWebImage,那么Swift中呢,如果要加载远程网络图片呢?这里接下来几篇就给大家介绍一个Swift中用于加载网络图片的框架 —— Nuke。
概览
首先我们看一下作者
它是强大的图像加载和缓存系统。 它使得将图像加载到视图中的任务非常简单,同时还支持更高要求的应用程序的高级功能。
快速LRU内存缓存,本机HTTP磁盘缓存和自定义主动LRU磁盘缓存
逐步图像加载(渐进式JPEG和WebP)
可恢复的下载,请求优先级,重复数据删除,速率限制等
使用Preheat自动prefetching(在iOS 10中已弃用)
Getting Started
详细Image Pipeline描述
整个部分致力于Performance
Contributing和路线图
更多信息可在Documentation目录和完整的API Reference中找到。 当您准备好安装Nuke
时,您可以遵循Installation Guide - 支持所有主要的包管理器。
Quick Start
1. Load Image into Image View
您可以使用一行代码将图像加载到图像视图中。
Nuke.loadImage(with: url, into: imageView)
Nuke将自动加载图像数据,在后台解压缩,将图像存储在内存缓存中并显示它。
要学习
ImagePipeline
,see the dedicated section
当您为视图请求新图像时,先前未完成的请求将被取消,图像将设置为nil
。 视图dealloc
时,请求也会自动取消。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { ... Nuke.loadImage(with: url, into: cell.imageView) ... }
2. Placeholders, Transitions and More
使用options
参数(ImageLoadingOptions)
自定义图像的加载和显示方式。 您可以提供占位符,选择其中一个内置过渡(transition)
或提供自定义过渡。 使用过渡时,请注意UIKit可能会保留对图像的引用,以防止因为长动画或一次加载多个过渡而被移除。
Nuke.loadImage( with: url, options: ImageLoadingOptions( placeholder: UIImage(named: "placeholder"), transition: .fadeIn(duration: 0.33) ), into: imageView)
有一个很常用的场景:当占位符(或失败的图像)需要以与用于加载的图像不同的content mode
显示时。
let options = ImageLoadingOptions( placeholder: UIImage(named: "placeholder"), failureImage: UIImage(named: "failure_image"), contentModes: .init( success: .scaleAspectFill, failure: .center, placeholder: .center ) ) Nuke.loadImage(with: url, options: options, into: imageView)
要使应用程序中的所有图像视图共享相同的行为,请修改ImageLoadingOptions.shared
。
如果
ImageLoadingOptions
缺少您需要的功能,请直接使用ImagePipeline
。 如果您认为每个人都可以从此功能中受益,PRs
是不错的选择。
3. Image Requests
每次请求都是ImageRequest
表示的,一次请求可以有URL
或者URLRequest
创建。
var request = ImageRequest(url: url)// var request = ImageRequest(urlRequest: URLRequest(url: url))// Change memory cache policy:request.memoryCacheOptions.isWriteAllowed = false// Update the request priority:request.priority = .high Nuke.loadImage(with: request, into: imageView)
4. Process an Image
使用特殊的ImageRequest初始化器改变图片的尺寸。
// Target size is in pixels.ImageRequest(url: url, targetSize: CGSize(width: 640, height: 320), contentMode: .aspectFill)
使用processed(key:closure:)
方法执行自定义转换。 以下是使用Toucan创建circular avatar
的方法。
ImageRequest(url: url).process(key: "circularAvatar") { Toucan(image: $0).maskWithEllipse().image }
所有这些API都建立在ImageProcessing
协议之上,您也可以使用它来实现自定义处理器。 请记住,ImageProcessing
还需要遵守Equatable
,这有助于Nuke识别内存缓存中的图像。
Nuke中使用Core Image,请参考Core Image Integration Guide
Advanced Usage
1. Image Pipeline
直接使用ImagePipeline加载图像,不用view
let task = ImagePipeline.shared.loadImage( with: url, progress: { _, completed, total in print("progress updated") }, completion: { response, error in print("task completed") } )
任务可用于监视下载进度,取消请求以及动态更新下载优先级。
task.cancel()task.setPriority(.high)
有关ImagePipeline的更多信息,请see the dedicated section
2. Configuring Image Pipeline
除了使用共享的ImagePipeline
实例外,您还可以创建自己的实例。
let pipeline = ImagePipeline { $0.dataLoader = ... $0.dataLoadingQueue = ... $0.imageCache = ... ... }// When you're done you can make the pipeline a shared one:ImagePipeline.shared = pipeline
3. Memory Cache
默认Nuke
的ImagePipeline
有两个缓存层。
首先,有一个缓存专门用来存储用来准备显示的处理过的图像。 您可以直接访问此缓存:
// Configure cacheImageCache.shared.costLimit = 1024 * 1024 * 100 // 100 MBImageCache.shared.countLimit = 100ImageCache.shared.ttl = 120 // Invalidate image after 120 sec// Read and write imageslet request = ImageRequest(url: url) ImageCache.shared[request] = imagelet image = ImageCache.shared[request]// Clear cacheImageCache.shared.removeAll()
4. HTTP Disk Cache
为了存储未处理的图像数据,Nuke使用URLCache
实例:
// Configure cacheDataLoader.sharedUrlCache.diskCapacity = 100DataLoader.sharedUrlCache.memoryCapacity = 0// Read and write responseslet request = ImageRequest(url: url)let _ = DataLoader.sharedUrlCache.cachedResponse(for: request.urlRequest) DataLoader.sharedUrlCache.removeCachedResponse(for: request.urlRequest)// Clear cacheDataLoader.sharedUrlCache.removeAllCachedResponses()
5. Aggressive Disk Cache
自定义LRU
磁盘缓存可用于快速可靠的主动数据缓存(忽略HTTP cache control)。 您可以使用管道配置启用它。
$0.dataCache = try! DataCache(name: "com.myapp.datacache")// On Swift 4.1 and lower you'll also need to provide a `FilenameGenerator`.
如果启用了主动磁盘缓存,请确保还禁用本机URL缓存(请参阅DataLoader
),否则最终可能会存储两次相同的映像数据。
DataCache
类型实现公共DataCaching
协议,可用于实现自定义数据缓存。
6. Prefetching Images
预先Prefethcing图像可以减少用户的等待时间。 Nuke
提供了一个ImagePreheater
来做到这一点:
let preheater = ImagePreheater() preheater.startPreheating(with: urls)// Cancels all of the preheating tasks created for the given requests.preheater.stopPreheating(with: urls)
需要权衡,prefetching
会占用用户的数据,并对CPU和内存造成额外压力。 要减少CPU和内存使用量,您可以选择仅将磁盘缓存作为prefetching
目标:
// 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)
为确保prefetching
请求不会干扰正常请求,最好降低其优先级。
您可以将Nuke
与Preheat库结合使用,该库可自动preheating
UICollectionView
和UITableView
中的内容。 在iOS 10.0上,您可能希望使用iOS提供的新prefetching APIs。
7. Progressive Decoding
要使用渐进式图像加载,您需要启用渐进式解码的管道。
let pipeline = ImagePipeline { $0.isProgressiveDecodingEnabled = true}
就是这样,你可以开始观察管道产生的图像。 进度处理程序也可用作渐进式图像处理程序。
let imageView = UIImageView()let task = ImagePipeline.shared.loadImage( with: url, progress: { response, _, _ in imageView.image = response?.image }, completion: { response, _ in imageView.image = response?.image } )
请参阅
"Progressive Decoding"
演示,了解渐进式JPEG在实践中的应用。
8. Animated Images
Nuke
使用animatedImageData
属性扩展UIImage
。 如果通过将ImagePipeline.Configuration.isAnimatedImageDataEnabled
设置为true
来启用它,则管道将开始将原始图像数据附加到动画图像(内置解码器现在仅支持GIF)。
在计算缓存项目的成本时,
ImageCache
会将animatedImageData
考虑在内。ImagePipeline
不会将处理器应用于带有动画数据的图像。
没有内置的方法来渲染这些图像,但有两种可用的集成:FLAnimatedImage和Gifu,它们既快速又高效。
GIF
不是传输和显示动画图像的最有效格式。 目前的最佳做法是use short videos instead of GIFs(例如MP4
,WebM
)。 演示项目中有一个PoC
,它使用Nuke
加载,缓存和显示MP4视频。
9. WebP
WebP
支持由Ryo Kosuge构建的Nuke WebP Plugin插件提供。 请按照repo
中的说明进行安装。
10. RxNuke
RxNuke为Nuke添加了RxSwift扩展,并支持许多常见用例:
And more...
以下是将go flow log
加载到高分辨率是多么容易的示例:
let pipeline = ImagePipeline.shared Observable.concat(pipeline.loadImage(with: lowResUrl).orEmpty, pipeline.loadImage(with: highResUtl).orEmpty) .subscribe(onNext: { imageView.image = $0 }) .disposed(by: disposeBag)
Image Pipeline
Nuke
的图像管道由大约五个阶段组成,可以使用以下协议进行自定义:
作者:刀客传奇
链接:https://www.jianshu.com/p/d2a34eb67d8c