前言
在源码解析的第一篇中,我们讲解了 Kingfisher 的最主要的功能-异步加载网络图片的流程,以及如何设计支持同一个类型参数可以根据 URL 类型加载本地以及网络的资源。
第二篇中,我们讲解了它如何实现内存、磁盘双缓存以及缓存的详细配置参数。
这一篇我们接着来根据 Features 继续梳理代码。
Cancelable downloading and auto-reusing previous downloaded content to improve performance.
当我们加载列表中的图片时,可以在 UITableView
或者 UICollectionView
的代理方法中直接对 cell
的 imageView
直接调用 setImage()
来异步加载网络资源。下面以 UICollectionView
为例子,写一下示例代码:
swift
override func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "collectionViewCell",
for: indexPath) as! ImageCollectionViewCell
let url = ImageLoader.sampleImageURLs[indexPath.row]
cell.cellImageView.kf.setImage(with: url)
return cell
}
这样是完全可以实现我们的需求的。但它也存在一个问题,那就是当你快速滑动的时候,当前屏幕加载的图片可能还未加载完就已经滑出屏幕了。也就是说你消耗的系统资源并不是百分百有用的,因为屏幕外的图片加载你也在执行,但这部分对用户来讲是无效的。我们可以结合 Kingfisher 的取消下载和预加载的功能来解决这个问题。
对应 UICollectionView
来说,我们可以在 didEndDisplaying
的代理方法中取消下载,在 willDisplay
的代理方法中预加载。示例代码如下:
swift
override func collectionView(
_ collectionView: UICollectionView,
didEndDisplaying cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
(cell as! ImageCollectionViewCell).cellImageView.kf.cancelDownloadTask()
}
override func collectionView(
_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
let imageView = (cell as! ImageCollectionViewCell).cellImageView!
let url = ImageLoader.sampleImageURLs[indexPath.row]
KF.url(url).set(to: imageView)
}
Independent components. Use the downloader, caching system, and image processors separately as you need.
通过项目源码的文件分层我们也可以看出来,Kingfisher 的模块设计做的是非常好的。它的资源加载、缓存或者图片处理器都是可以单独使用的。不一定非得全套使用。
比如,在自己的项目中,经常会碰到一些数据缓存的功能。这时,你就可以单独使用它的缓存功能来实现,这样就不用自己再写一套缓存机制了。示例代码如下(以 String 为例):
swift
extension String: CacheCostCalculable {
public var cacheCost: Int {
return Data(self.utf8).count
}
}
let totalMemory = ProcessInfo.processInfo.physicalMemory
let costLimit = totalMemory / 4
let memoryStorage = MemoryStorage.Backend<String>(config:
.init(totalCostLimit: (costLimit > Int.max) ? Int.max : Int(costLimit)))
memoryStorage.store(value: "测试数据", forKey: "test_key")
因为它要求存入的值遵守 CacheCostCalculable
协议以计算缓存的大小,所以我们需要给 String
的扩展中实现一下 cacheCost
计算属性。
之后的使用代码可以看出来,跟别的模块是没有什么耦合的。
Prefetching images and showing them from the cache to boost your app.
这一条跟上面的预加载是一个意思。
Extensions for UIImageView
, NSImageView
, NSButton
, UIButton
, NSTextAttachment
, WKInterfaceImage
, TVMonogramView
and CPListItem
to directly set an image from a URL.
Kingfisher 不仅仅给 UIImageView
添加了扩展属性 kf
,它还给按钮、文本等控件添加了扩展属性。源码如下:
css
extension KFCrossPlatformImage: KingfisherCompatible { }
extension KFCrossPlatformImageView: KingfisherCompatible { }
extension KFCrossPlatformButton: KingfisherCompatible { }
extension NSTextAttachment: KingfisherCompatible { }
Built-in transition animation when setting images.
Kingfisher 内部提供了很多过度动画,可以在网络资源加载完的时候展示,支持类型如下:
java
public enum ImageTransition {
case none
case fade(TimeInterval)
case flipFromLeft(TimeInterval)
case flipFromRight(TimeInterval)
case flipFromTop(TimeInterval)
case flipFromBottom(TimeInterval)
case custom(duration: TimeInterval,
options: UIView.AnimationOptions,
animations: ((UIImageView, UIImage) -> Void)?,
completion: ((Bool) -> Void)?)
}
读源码可以看到,不仅支持 5 中类型,还可以自定义自己想实现的类型。该功能对 UI 美观度要求高的 APP 开发者还是很有友好的。
动画的具体实现看源码可以知道,本质上是对 UIView.AnimationOptions
的一层封装:
swift
var animationOptions: UIView.AnimationOptions {
switch self {
case .none: return []
case .fade: return .transitionCrossDissolve
case .flipFromLeft: return .transitionFlipFromLeft
case .flipFromRight: return .transitionFlipFromRight
case .flipFromTop: return .transitionFlipFromTop
case .flipFromBottom: return .transitionFlipFromBottom
case .custom(_, let options, _, _): return options
}
}
每个动画具体的效果图,大家可以运行一下官方的 Demo 看一下。具体的逻辑在这个控制器:TransitionViewController
。
Customizable placeholder and indicator while loading images.
支持自定义占位图和指示器。
java
public enum IndicatorType {
case none
case activity
case image(imageData: Data)
case custom(indicator: Indicator)
}
Extensible image processing and image format easily.
支持多种图片格式,比如:PNG、JPEG、GIF等。详情可参考 官方的 Demo。
Low Data Mode support.
支持在 Low Data Mode 的情况下,加载低分辨率的图片或者加载本地图片。
SwiftUI support.
Kingfisher 也适配了 SwiftUI,在你的 SwiftUI 项目中也可以使用它了。 示例代码如下 - KFImage
:
scss
var body: some View {
HStack(alignment: .center) {
Spacer()
KFImage.url(url)
.resizable()
Spacer()
}.padding(.vertical, 12)
}
到这里。Kingfisher 的源码阅读就全部完成了,如果本系列文章有哪里写的不对的地方,还请读者指正。