1. 调用方法解析
public func setImage(
with resource: Resource?,
placeholder: Placeholder= nil
options: KingfisherOptionsInfo= nil,
completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)= nil) -> DownloadTask?
参数:
- resource:目标资源;需实现Resource协议,URL继承了Resource协议,所以可以直接用URL对象
- placeholder:站位图;需实现
Placeholder
协议,可自定义一个CustomView实现Placeholder
协议,将这个CustomView作为站位图显示 - options:设置图片的一些可选项的集合;
- completionHandler:设置图片完成的回调
使用案例:
guard let url = URL(string: "https://pic.huitu.com/res/20221125/754309_20221125075727864209_1.jpg") else {
return
}
let originalCache = ImageCache(name: "original_Cache")
let targetCache = ImageCache(name: "target_Cache")
let processor = RoundCornerImageProcessor(cornerRadius: 50) |> BlurImageProcessor(blurRadius: 5)
let options: [KingfisherOptionsInfoItem] = [
.transition(.flipFromBottom(0.3)), // 从底部翻转动画
.forceTransition, // 每次都要执行翻转动画
.processor(processor), // 添加processor
.onFailureImage(UIImage(named: "hot")), // 下载/找回图片失败,显示hot图片
.targetCache(targetCache), // 指定被处理后图片缓存
.originalCache(originalCache), // 指定原始图片缓存
.cacheOriginalImage, // 缓存原始图片
.waitForCache, // 等待缓存结束后在执行completionBlock
.memoryCacheExpiration(.seconds(20)), // 设置该图片在内存缓存过期时间
.diskCacheExpiration(.seconds(20)) // 设置该图片在磁盘缓存过期时间
]
iv.kf.setImage(with: url, options: options) { result in
switch result {
case .success(let img):
// ...
case .failure(let error):
// ...
}
}
2. KingfisherOptionsInfoItem - 配置选项
-
targetCache(ImageCache)
指定kingfisher使用的cache对象,会被用来找回缓存的图片和存储下载的图片。
-
originalCache(ImageCache)
用来存储初始图片的cache对象,会被用来找回缓存的图片和存储下载的初始图片,如果没有设置
originalCache(ImageCache)
,则会去targetCache(ImageCache)
对象中查找初始图片。在下载和存储图片的过程中,如果设置了
cacheOriginalImage
这个option,就会将初始图片缓存在originalCache中;如果在targetCache
对象中没有找到经过professor处理的图片,则会去originCache
中查找原始图片,如果在originalCache中找到了原始图片,则会将该图片根据professor进行处理。这样能有效防止重复下载图片。 -
downloader(ImageDownloader)
指定用来下载图片的ImageDownloader对象
-
transition(ImageTransition)
如果该图片是需要从网络下载的,Kingfisher会根据设置的ImageTransition枚举对象来对这个图片进行动画。如果是从缓存或者磁盘中找回的,则无动画。如果想要每次都执行动画,则需要设置
.forceRefresh
选项,使图片永远是下载的;或者使用.forceTransition
,哪怕是从缓存中获取的图片,也会执行动画。 -
downloadPriority(Float)
设置该图片下载的优先级,值的范围是0 - 1,如果没有设置这个选项,则默认使用
URLSessionTask.defaultPriority
-
forceRefresh
忽略本地缓存,重新下载
-
fromMemoryCacheOrRefresh
如果设置了这个选项,Kingfisher会优先从缓存中找回图片,如果在缓存中没找到,则会直接开始重新下载,不会再去磁盘中进行查找。
当我们的图片资源发生变化,但是图片的url并没有发生变化时,可使用该选项,能有效防止多次重复下载。
-
cacheMemoryOnly
只将图片存储在缓存中
-
waitForCache
Kingfisher只会在缓存操作结束后再执行completion回调
-
onlyFromCache
只会在缓存中找回图片,如果没找到,不会通过网络下载,而是直接报错:
KingfisherError.cacheError(reason: .imageNotExisting(key: source.cacheKey))
-
backgroundDecode
在使用图片前,会启用子线程对下载的图片进行解析,并且通过离屏渲染获取像素信息。这会使得展示的速度更快,但是要消耗更多的时间来做使用前的准备
-
callbackQueue(CallbackQueue)
从缓存中找回图片后的回调,将会在该queue中进行操作,如果没有设置,则默认使用
.mainCurrentOrAsync
。这个queue不会影响UI相关的扩展方法的回调所在的队列,UI类的扩展方法的回调都是在主队列中进行的。
-
scaleFactor(CGFloat)
在将找回的data转换成图片时,使用这个关联值来设置图片的scale;比如加载2x和3x图片时,可以使用这个option来设置,如果不设置这个option,Kingfisher会按照scale为1.0去讲data转为image
-
preloadAllAnimationData
整个的动图data是否被预加载。默认是不预加载整个的动图数据的,只是按需加载下一帧;如果设置为true,则会将整个动图的data加载并解码到内存中;
这个option主要用于内部的向后兼容。开发时,不能直接设置这个option,而是应该选择imageView的相关类来控制data的加载。在Kingfisher中,有两个类用来展示动图,
AnimatedImageView
和UIImageView
;AnimatedImageView
不会预加载所有动图data,在加载动图的过程中,它消耗更少的内存,但会消耗更多的CPU。UIImageView
会立即加载整个动图的data并解码存储到内存中,所以会消耗更多的内存,但只对所有的图片帧解码一次。 -
requestModifier(AsyncImageDownloadRequestModifier)
可以通过关联值
AsyncImageDownloadRequestModifier
对象来设置request,这是最后一次修改下载图片request的机会。比如我们可以在这里为header添加auth token,url映射等。 -
redirectHandler(ImageDownloadRedirectHandler)
关联值
ImageDownloadRedirectHandler
用来在重定向之前去设置request,比如:我们可以在这里为header添加auth token,url映射等。 -
processor(ImageProcessor)
在图片下载完成后,关联值
ImageProcessor
会被用来将下载的data处理为image,并添加一些效果。如果下载器关联了某个cache对象(通过KingfisherManager或者UIImageView的扩展方法触发的下载都有cache对象),转换后的image也会被缓存下来。可联系上面的targetCache(ImageCache)
和originCache(ImageCache)
两个option。 -
cacheSerializer(CacheSerializer)
在关联值
CacheSerializer
会被用来将data处理为Image,以便从磁盘缓存中找回或从缓存到磁盘缓存中 -
imageModifier(ImageModifier)
关联值
ImageModifier
用来在使用图片之前修改图片的。如果图片是从下载器中获取的,关联值modifier会在ImageProcessor
之后被立即调用;如果是从缓存中找回的,关联值modifier
将会在CacheSerializer
之后被调用。当你需要改变图片,但是又不希望这些改变被和图片一起缓存起来,则可以通过这个option来设置,比如renderingModel(原色显示、根据tintColor显示)或者alignmentInsets(剔除装饰如阴影、徽章,圈定核心区域,自动布局也是根据核心区域去进行的)
-
keepCurrentImageWhileLoading
在设置其它图片的过程中(如下载),保留当前图片。设置了这个属性之后,站位图会被忽略。
-
onlyLoadFirstFrame
在加载动图时,只加载第一帧。因为加载动图时比较耗费内存的,所以可以加载第一帧作为预览图片。
如果不是加载动图,这个option设置了也会被忽略。
-
cacheOriginalImage
初始图片只会被缓存到磁盘存储。
在下载时,如果设置了
ImageProcessor
并且该processor
被使用到了,Kingfisher会缓存初始图片和经过处理的最终图片,因此,当其他processor对该图片进行处理时,就不需要从网络下载了。可以结合originalCache option来指定初始图片资源的缓存。 -
onFailureImage(KFCrossPlatformImage?)
关联属性是个image,当下载或者从缓存中找回图片失败时,会使用这个image填充到ImageView中。
适用于不想设置站位图,但是加载图片失败了,又想展示一个设定的图片的情况。
-
alsoPrefetchToMemory
如果使用了
ImagePrefetcher
,预加载系统会主动将图片加载到内存中。预加载时如果图片已经被存放在磁盘中,从磁盘获取的图片也要缓存到内存中 -
loadDiskFileSynchronously
同步加载磁盘图片文件。
原本地盘加载图片在自己的异步队列中进行,但是如果imageView已经有图片了,再从磁盘读取image并设置给imageView,会导致闪烁。设置这个option,通常会让所有的加载在UI线程中进行,能防止产生闪烁,但会消耗性能。
-
diskStoreWriteOptions(Data.WritingOptions)
在将data写入磁盘存储时,关联对象用来设置写入选项
-
memoryCacheExpiration(StorageExpiration)
设置内存缓存的过期时间
-
memoryCacheAccessExtendingExpiration(ExpirationExtending)
从内存读取图片时,设置延长过期时间的策略
none
:保持原来的过期时间`cacheTime:刷新过期时间,如原来是缓存成功3天后过期,本次访问过后,从当前时间开始,3天后过期
expirationTime(_ expiration: StorageExpiration)
:设置过期时间为指定的时间,如10秒后
-
diskCacheExpiration(StorageExpiration)
设置磁盘缓存的过期时间
-
diskCacheAccessExtendingExpiration(ExpirationExtending)
同上面的memoryCacheAccessExtendingExpiration(ExpirationExtending)
-
processingQueue(CallbackQueue)
指定处理图片的队列。
默认情况下,Kingfisher使用预定义的串行队列来处理图片。比如:使用
.mainCurrentOrAsync
来处理图片,能防止设置闪烁,但是如果处理图片耗时较长,会阻塞主线程UI。 -
progressiveJPEG(ImageProgressive)
支持渐进式图片,关联值为针对渐进式图片的处理。
-
alternativeSources([Source])
加载失败时,提供可替换的数据源。
当初始请求失败,则会根据关联值[Source]开始新的加载请求。如果某一个请求成功,则不会继续请求后面的Source;如果所有的备用Source都请求失败了,则会抛出错误
-
retryStrategy(RetryStrategy)
设置重试策略。
当通过
KingfisherManager
找回图片时,如果发生了错误,会使用关联值进行重试。UIImageView和UIButton通过kf调用的的扩展方法都是通过KingfisherManager
进行的,所以重试策略在这两个情况下可以生效。但是重试策略不支持ImageDownloader
或者ImageCache
。 -
lowDataMode(Source?)
设置低数据模式。
当用户打开了低数据模式并且初始请求报错:
NSURLErrorNetworkUnavailableReason.constrained
,此时如果设置了这个option,关联值Source将会被用于进行低数据模式的请求,此外,可以让服务端提供低像素的图片来作为低数据模式获取的资源。
3. ImageProcessor - 图片处理器
通过processor(ImageProcessor)设置ImageProcessor会对下载的原始图片进行处理并被缓存。
// 圆角
// let processor = RoundCornerImageProcessor(cornerRadius: 20)
// 降低采样率,会使图片变模糊
// let processor = DownsamplingImageProcessor(size: CGSize(width: 50, height: 50))
// 裁剪
// let processor = CroppingImageProcessor(size: CGSize(width: 100, height: 100), anchor: CGPoint(x: 0.5, y: 0.5))
// 毛玻璃
// let processor = BlurImageProcessor(blurRadius: 10)
// 在图片表面覆盖一层颜色
// let processor = OverlayImageProcessor(overlay: .white, fraction: 0.7)
// 用一种颜色进行着色,会覆盖原图
// let processor = TintImageProcessor(tint: .blue)
// 饱和度、对比度等调整
// let processor = ColorControlsProcessor(brightness: 1.0, contrast: 0.7, saturation: 1.1, inputEV: 0.7)
// 黑白图
// let processor = BlackWhiteProcessor()
// 混合颜色
// let processor = BlendImageProcessor(blendMode: .colorBurn, alpha: 0.9, backgroundColor: .cyan)
// 重设图片大小
// let processor = ResizingImageProcessor(referenceSize: CGSize(width: 10, height: 10))
设置方法:
let processor = RoundCornerImageProcessor(cornerRadius: 20)
let options = [.processor(processor)]
iv.kf.setImage(with: url, options: options) { result in
// ...
}
多个processor结合使用
let processor = RoundCornerImageProcessor(cornerRadius: 20) |> BlurImageProcessor(blurRadius: 10)
let options = [.processor(processor)]
iv.kf.setImage(with: url, options: options) { result in
// ...
}
4. Cache - 缓存
Kingfisher使用ImageCache类来控制缓存,它采用混合缓存的模式,包含内存缓存和磁盘缓存。
-
指定图片的cacheKey
let resource = ImageResource(downloadURL: url, cacheKey: "my_cache_key") imageView.kf.setImage(with: resource)
-
检查图片是否被缓存
let cached = ImageCache.default.isCached(forKey: cacheKey)
-
检查图片被缓存的位置
let cacheType = cache.imageCachedType(forKey: cacheKey) // .memory, .disk, .none
-
从缓存中找回图片
cache.retrieveImage(forKey: "cacheKey") { result in switch result { case .success(let value): print(value.cacheType) // If the `cacheType is `.none`, `image` will be `nil`. print(value.image) case .failure(let error): print(error) } }
-
在下载或者从缓存找回图片时,使用
processor
对图片进行了一些处理,则经过处理的图片会被缓存下来,所以在获取该图片时,不仅需要图片的cacheKey
,还需要processor
的identifier
,这样才能从缓存中找到经过处理的图片。let processor = RoundCornerImageProcessor(cornerRadius: 20) imageView.kf.setImage(with: url, options: [.processor(processor)])
判断是否缓存了经过处理的图片
cache.isCached(forKey: cacheKey, processorIdentifier: processor.identifier)
找回经过处理的图片
cache.retrieveImage(forKey: cacheKey, options: KingfisherParsedOptionsInfo([processor])) { result in switch result { case .success(let value): print(value.cacheType) // If the `cacheType is `.none`, `image` will be `nil`. print(value.image) case .failure(let error): print(error) } }
-
设置cache的内存大小
设置内存缓存大小
// 设置缓存内存最大为 300 MB. cache.memoryStorage.config.totalCostLimit = 300 * 1024 * 1024 // 最多缓存150张图片 cache.memoryStorage.config.countLimit = 150
设置磁盘缓存大小
// 设置磁盘缓存内存最大 1 GB. cache.diskStorage.config.sizeLimit = = 1000 * 1024 * 1024
-
设置缓存过期时间
设置cache对象下的所有内存缓存过期时间
// 内存缓存10分钟后过期 cache.memoryStorage.config.expiration = .seconds(600) // 磁盘缓存永不过期 cache.diskStorage.config.expiration = .never
针对某张图片设置过期时间,内存缓存和磁盘缓存都可设置,还可以在访问缓存图片时设置
memoryCacheAccessExtendingExpiration
option来延长缓存时间imageView.kf.setImage(with: url, options: [.memoryCacheExpiration(.never)]) // 在获取图片时延长缓存时间 cache.retrieveImageInDiskCache(forKey: url.cacheKey, options: [.diskCacheAccessExtendingExpiration(.expirationTime(.seconds(100)))]) { result in // ... }
-
清理缓存
-
内存缓存每隔2分钟会清理一下缓存,如果想更频繁地清理缓存,可设置
cache.memoryStorage.config.cleanInterval = 30
-
清理某一张图片
cache.default.removeImage(forKey: cacheKey)
或者调用更强大的方法
cache.removeImage( forKey: cacheKey, processorIdentifier: processor.identifier, fromMemory: false, fromDisk: true) { print("Removed!") }
-
清理所有缓存
cache.clearMemoryCache() cache.clearDiskCache { print("Done") }
-
清理过期缓存
cache.cleanExpiredMemoryCache() cache.cleanExpiredDiskCache { print("Done") }
-
-
获取已用磁盘缓存大小
ImageCache.default.calculateDiskStorageSize { result in switch result { case .success(let size): print("Disk cache size: \(Double(size) / 1024 / 1024) MB") case .failure(let error): print(error) } }
-
自定义cache
let originalCache = ImageCache(name: "original_Cache") let targetCache = ImageCache(name: "target_Cache")
结合option使用
let options: [KingfisherOptionsInfoItem] = [ .cacheOriginalImage, // 存储原始图片 .originalCache(originalCache), // 设置存储原始图片的cache .targetCache(targetCache), // 设置目标cache,上面设置了originCache,这里会存放处理后的图片 .waitForCache, // 等待cache完成再执行completion closure .memoryCacheExpiration(.seconds(200)), // 设置内存缓存过期时间 .diskCacheExpiration(.seconds(400)) // 设置磁盘缓存过期时间 ]
上面的options实现了以下功能:
- 存储原始图片并设置原始图片存储的cache对象为originCache,此时原始图片是存储在磁盘缓存中的;
- 经过processor处理的图片是由targetCache负责存储的,可以结合processor.identifier在targetCache中找回图片
- 设置了图片在内存和磁盘中缓存的过期时间
5. Downloader - 下载器
-
一般情况下,建议使用UIImageView等的扩展方法或者
KingfisherManager
来加载图片,因为它们会将图片缓存到本地,防止重复下载;如果我们只是下载图片,不需要缓存图片,则可以这样:let downloader = ImageDownloader.default downloader.downloadImage(with: url) { result in switch result { case .success(let value): print(value.image) case .failure(let error): print(error) } }
-
取消下载
如果在取消下载前,下载已经完成,则不会有任何影响。
let task = downloader.downloadImage(with: url) { result in // ... case .failure(let error): print(error.isTaskCancelled) // true } } // 在下载完成前,取消下载 task?.cancel()
可以通过UI类的扩展方法来取消下载
let task = imageView.kf.set(with: url) task?.cancel()
还可以通过调用
cancelDownloadTask
方法来取消下载let task1 = imageView.kf.set(with: url1) let task2 = imageView.kf.set(with: url2) imageView.kf.cancelDownloadTask()
task2会被取消,但是task1还是会执行。
但是task1下载的图片不会被设置到ImageView上,因为ImageView期望的是通过url2返回的图片
-
自定义对服务端challenge的回应
ImageDownloader有一个默认的.performDefaultHandling来应对服务端的挑战,我们也可以自定义对服务端挑战的回应
// In ViewController ImageDownloader.default.authenticationChallengeResponder = self extension ViewController: AuthenticationChallengeResponsable { var disposition: URLSession.AuthChallengeDisposition { /* */ } let credential: URLCredential{ /* */ } func downloader( _ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // Provide your `AuthChallengeDisposition` and `URLCredential` completionHandler(disposition, credential) } func downloader( _ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // Provide your `AuthChallengeDisposition` and `URLCredential` completionHandler(disposition, credential) } }
-
设置下载超时时间
// 设置超时时间为1分钟 downloader.downloadTimeout = 60
-
对某一个特定的下载设置超时时间
let modifier = AnyModifier { request in var r = request r.timeoutInterval = 60 return r } downloader.downloadImage(with: url, options: [.requestModifier(modifier)])
6. Serializer - 序列化
序列化指的就是将Image转换成data以便进行缓存,或将data转换成Image以便展示在页面上。
默认情况下使用的是DefaultCacheSerializer
,它支持PNG,JPEG和GIF。如果我们想支持WEBP,可以通过实现CacheSerializer
协议来自定义一个Serializer:
struct WebpCacheSerializer: CacheSerializer {
func data(with image: Image, original: Data?) -> Data{
return WebpFramework.webpData(of: image)
}
func image(with data: Data, options: KingfisherParsedOptionsInfo?) -> Image{
return WebpFramework.createImage(from: webpData)
}
}
// 使用
let serializer = WebpCacheSerializer()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.cacheSerializer(serializer)])
7. Prefetch - 预获取图片
如果我们确定接下来将要使用一些图片 ,那么我们可以使用预获取功能
let urls = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
.map { URL(string: UICollectionViewDataSourcePrefetching
)! }
let prefetcher = ImagePrefetcher(urls: urls) {
skippedResources, failedResources, completedResources in
print("These resources are prefetched: \(completedResources)")
}
prefetcher.start()
// 使用方法
imageView.kf.setImage(with: urls[0])
anotherImageView.kf.setImage(with: urls[1])
可以结合iOS10以后提供的UITableViewDataSourcePrefetching
和override func viewDidLoad() {
super.viewDidLoad()
collectionView?.prefetchDataSource = self
}
extension ViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
let urls = indexPaths.flatMap { URL(string:
来使用:8. ImageDataProvider - 以其它形式加载图片
.urlString) }
ImagePrefetcher(urls: urls).start()
}
}
let url = URL(fileURLWithPath: path)
let provider = LocalFileImageDataProvider(fileURL: url)
// 结合processor
let processor = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: provider, options: [.processor(processor)])
let provider = Base64ImageDataProvider(base64String: "\/9j\/4AAQSkZJRgABAQA...", cacheKey: "some-cache-key")
imageView.kf.setImage(with: provider)
Kingfisher还支持从本地data获取图片,并结合processor来对图片进行加工处理。
-
从本地url获取图片
Video URL
-
从base64获取图片
AVAsset
-
从
let provider = AVAssetImageDataProvider( assetURL: URL(string: "https://example.com/your_video.mp4")!, seconds: 15.0 )
或ImageDataProvider
的指定时间生成一张图片cacheKey
-
自定义ImageDataProvider
遵守
data(handler:)
协议需要实现data(handler:)
和struct UserNameLetterIconImageProvider: ImageDataProvider { var cacheKey: String { return letter } let letter: String init(userNameFirstLetter: String) { self.letter = userNameFirstLetter } func data(handler: @escaping (Result<Data, Error>) -> Void) { // You can ignore these detail below. // It generates some data for an image with `letter` being rendered in the center. let letter = self.letter as NSString let rect = CGRect(x: 0, y: 0, width: 250, height: 250) let renderer = UIGraphicsImageRenderer(size: rect.size) let data = renderer.pngData { context in UIColor.black.setFill() context.fill(rect) let attributes = [ NSAttributedString.Key.foregroundColor: UIColor.white, .font: UIFont.systemFont(ofSize: 200) ] let textSize = letter.size(withAttributes: attributes) let textRect = CGRect( x: (rect.width - textSize.width) / 2, y: (rect.height - textSize.height) / 2, width: textSize.width, height: textSize.height) letter.draw(in: textRect, withAttributes: attributes) } // Provide the image data in `handler`. handler(.success(data)) } } // Set image for user "John" let provider = UserNameLetterIconImageProvider(userNameFirstLetter: "J") imageView.kf.setImage(with: provider)
方法,9. Indicator - 加载指示器
方法的参数handler是一个闭包,闭包的参数是Result类型,所以,我们需要将经过处理的最终image data传递给Result。let path = Bundle.main.path(forResource: "loader", ofType: "gif")! let data = try! Data(contentsOf: URL(fileURLWithPath: path)) imageView.kf.indicatorType = .image(imageData: data) imageView.kf.setImage(with: url)
struct MyIndicator: Indicator {
let view: UIView = UIView()
func startAnimatingView() { view.isHidden = false }
func stopAnimatingView() { view.isHidden = true }
init() {
view.backgroundColor = .red
}
}
// 调用
let i = MyIndicator()
imageView.kf.indicatorType = .custom(indicator: i)
-
使用gif作为指示器
imageView.kf.setImage(with: url, progressBlock: { receivedSize, totalSize in let percentage = (Float(receivedSize) / Float(totalSize)) * 100.0 print("downloading progress: \(percentage)%") myIndicator.percentage = percentage })
-
自定义的Indicator
Content-Length
-
自定义Indicator结合progressBlock使用
10. Retry - 重试
注意:只有当服务端返回的response的header中包含
// 最多重试5次,每次间隔3秒 let retry = DelayRetryStrategy(maxRetryCount: 5, retryInterval: .seconds(3)) // 结合option使用 imageView.kf.setImage(with: url, options: [.retryStrategy(retry)])
时,progressBlock才会执行。
.second(3)
DelayRetryStrategy.Interval
accumulated(3)
是的一个枚举项,还可以选择,这样重试的触发间隔时间就是3, 6, 9, 12, ...