序言
Kingfisher 是一个下载、缓存网络图片的轻量级纯swift库, 作者@王巍自称是受著名三方库SDWebImage激励所写,一年多以来,该库深受广大iOS之swift开发者所喜爱,目前被很多iOS开发者应用在app中。在swift中它真的是一个SDWebImage的升级版,作为swift开发者来说,为了摒弃Objective-C的风格,甚至“断绝”与Objective-C的关系,使工程更swift化,我们更希望更喜欢使用纯净的swift来开发自己的app。在此,也非常感谢@喵神给众多开发者提供了很大的便利,
为业界开发者所做的无私贡献。
github: [Kingfisher] (https://github.com/onevcat/Kingfisher)
特征
- 异步下载和缓存图片
- 基于
networking
的URLSession
, 提供基础的图片处理器和过滤器 - 内存和磁盘的多层缓存
- 可撤销组件,可根据需要分开地使用下载器和缓存系统
- 必要时可从缓存中读取并展示图片
- 扩展
UIImageView
、NSImage
、UIButton
来直接设置一个URL
图片 - 设置图片时,内置过渡动画
- 支持扩展图片处理和图片格式
先看一个Kingfisher的基本用法:从网络上设置一张基本的图片
let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg")
imageView.kf.setImage(with: url)
美女跃然屏上(模拟器截图)
Kingfisher从
url
下载图片,将图片存储在内存缓存和磁盘缓存,然后将它展示在imageView
上。当使用同样的代码后(url不变),就会直接从内存中获取之前缓存的图片并立即显示出来。
要求
- iOS 8.0+ / macOS 10.10+
- Swift 3(Kingfisher 3.x), Swift 2.3(Kingfisher 2.x)
Kingfisher 3.0迁移指导 : 从早期版本升级到Kingfisher 3.0以上
集成
注:这里只介绍CocoaPods
的集成方式
source 'https://gitgub.com/CocoaPods/Specs.git'
platform :ios, '8.0'
target '你的工程名' do
use_frameworks!
pod 'Kingfisher', '~> 3.0'
end
then
pod install
基本使用
直接设置一张url图片
if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
imgView.kf.setImage(with: url)
}
诠释:使用直接设置的方法,
Kingfisher
首先会尝试从缓存中去取,如果没有,则直接下载图片并且缓存下来以备后用。此外,Kingfisher
默认使用absoluteString of url
(即绝对url)作为cacheKey
以方便再次加载该图片的时候去缓存中根据cacheKey(也就是图片url)
查找,通俗来说就是把图片的整个链接作为cacheKey
来缓存在本地。
指定一个cacheKey
缓存图片
let imageResource = ImageResource(downloadURL: url, cacheKey: "Custom_cache_key")
imageView.kf.setImage(with: imageResource)
设置占位符图片
在Kingfisher
中,为了防止加载网络图片失败,提供了一个设置占位符图片功能,也就是说,当网络加载过程中或者图片加载失败时,就使用自定义的默认图片来代替显示。
if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
imageView.kf.setImage(with: url, placeholder: placeholder_image, options: nil, progressBlock: nil, completionHandler: nil)
}
下载完成回调
if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, imageUrl) in
image // 为 nil 时,表示下载失败
error // 为 nil 时,表示下载成功, 非 nil 时,就是下载失败的错误信息
cacheType // 缓存类型,是个枚举,分以下三种:
// .none 图片还没缓存(也就是第一次加载图片的时候)
// .memory 从内存中获取到的缓存图片(第二次及以上加载)
// .disk 从磁盘中获取到的缓存图片(第二次及以上加载)
imageUrl // 所要下载的图片的url
})
}
加载菊花
Kingfisher
提供了一个在下载图片过程中显示加载菊花的功能,图片加载成功后菊花自动消失,可以通过设置indicatorType
来显示菊花
IndicatorType
是一个枚举
public enum IndicatorType {
/// 默认没有菊花
case none
/// 使用系统菊花
case activity
/// 使用一张图片作为菊花,支持gif图
case image(imageData: Data)
/// 使用自定义菊花,要遵循Indicator协议
case custom(indicator: Indicator)
}
设置方式 1 : 使用系统菊花
imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: url)
演示效果(加载的是我微博上的一张图片)
设置方式 2 :使用gif图作为加载菊花
let path = Bundle.main.path(forResource: "myImage", ofType: "gif")!
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
imageView.kf.indicatorType = .image(imageData: data)
imageView.kf.setImage(with: url)
演示效果
设置方式 3 :自定义菊花,遵循 Indicator
协议
let myIndicator = CustomIndicator()
imageView.kf.indicatorType = .custom(indicator: myIndicator)
imageView.kf.setImage(with: url)
struct CustomIndicator: Indicator {
var view: IndicatorView = UIView()
func startAnimatingView() {
view.isHidden = false
}
func stopAnimatingView() {
view.isHidden = true
}
init() {
view.backgroundColor = UIColor.magenta
}
}
诠释:Indicator是一个协议,如果我们要实现自定义菊花加载,只需要遵循这个协议即可,另外必须得说的是,
Indicator协议
中有一个属性viewCenter
我们不需要在遵循协议的时候去实现,因为喵神已经为我们提供了默认实现(我还因为这个问题在github上问过他,突然觉得好蠢...)
设置方式 4 : 根据实时下载图片的数据做进度条加载或者菊花加载(灵活,比例为:图片已下载数据 / 图片总数据)
imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedData, totolData) in
let percentage = (Float(receivedData) / Float(totolData)) * 100.0
print("downloading progress is: \(percentage)%")
// 这里用进度条或者绘制view都可以,然后根据 percentage% 表示进度就行了
}, completionHandler: nil)
设置过渡动画,渐变效果(图片下载结束后)
imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(0.4))], progressBlock: nil, completionHandler: nil)
诠释: 在
Kingfisher
中,有一个options
, 我们可以做一些选择,Kingfisher里面目前包含了18种选择,在图片下载结束后,为了实现一些效果,我们可以实现上面的渐变效果,或者还有更多的翻滚效果! 但是Kingfisher
,上面的这个过渡动画值只是针对图片是从web下载时调用,如果是从内存或磁盘中取时是不会有这个效果的,虽然里面也有一个枚举值强制动画forceTransition
,但是似乎暂时作者还没有实现这个功能。
设置图片模糊效果
let processor = BlurImageProcessor(blurRadius: 25)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)], progressBlock: nil, completionHandler: nil)
诠释:还有很多其他选择,可自行根据需要设置。
强制下载(跳过缓存,直接从web下载图片)
imageView.kf.setImage(with: url, placeholder: nil, options: [.forceRefresh], progressBlock: nil, completionHandler: nil)
强制从缓存中获取图片(缓存中没有也不会从网络下载)
imageView.kf.setImage(with: url, placeholder: nil, options: [.onlyFromCache], progressBlock: nil, completionHandler: nil)
给按钮(UIButton)设置图片或背景图片
同设置UIImageView
一样, 我们也可以使用Kingfisher
来给UIButton
设置图片, 用法相同:
button.kf.setImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
button.kf.setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
缓存 和 下载器
Kingfisher
由两个主要组件组成:下载图片的图片下载器ImageDownloader
、操作缓存的图片缓存ImageCache
。我们也可以单独使用其中任意一个。
- 使用
ImageDownloader
下载图片(不可以缓存图片)
ImageDownloader.default.downloadImage(with: url, retrieveImageTask: nil, options: nil, progressBlock: { (receivedData, totalData) in
receivedData // 已接收图片数据
totalData // 图片总大小
}, completionHandler: { (image, error, url, data) in
image // 图片
error // 下载失败时的错误信息
url // 所要下载的图片的url
data // 下载完成后的总数据
})
- 使用
ImageCache
来存储或获取图片
存储图片:
let image: UIImage = ...
ImageCache.default.store(image, forKey: "Specified_Keys")
获取图片:
// 获取缓存检查结果
let cacheCheckResult = ImageCache.default.isImageCached(forKey: "Specified_Keys")
print("cacheCheckResult is \(cacheCheckResult)")
// 从磁盘获取图片
let img1 = ImageCache.default.retrieveImageInDiskCache(forKey: "Specified_Keys")
if let image = img1 {
imageView.image = image
}
print("Image1 in disk cache is \(String(describing: img1))")
// 从磁盘获取图片
let img2 = ImageCache.default.retrieveImageInDiskCache(forKey: "Specified_Keys", options: [.transition(.fade(0.5))])
print("Image2 in disk cache is \(String(describing: img2))")
// 从内存获取图片
let image1 = ImageCache.default.retrieveImageInMemoryCache(forKey: "Specified_Keys")
print("image1 in memory cache is \(String(describing: image1))")
// 从内存获取图片
let image2 = ImageCache.default.retrieveImageInMemoryCache(forKey: "Specified_Keys", options: [.transition(.fade(0.5))])
print("image2 in memory cache is \(String(describing: image2))")
// 直接获取图片(获取后才知道是 memory 还是 disk)
ImageCache.default.retrieveImage(forKey: "Specified_Keys", options: nil) { (image, cacheType) in
print("image is \(String(describing: image))")
print("cacheType is \(cacheType)")
self.imageView.image = image
}
演示效果:
打印结果:
test
cacheCheckResult is CacheCheckResult(cached: true, cacheType: Optional(Kingfisher.CacheType.memory))
Image1 in disk cache is Optional(<UIImage: 0x600000281b80>, {2448, 3264})
Image2 in disk cache is Optional(<UIImage: 0x600000281cc0>, {2448, 3264})
image1 in memory cache is Optional(<UIImage: 0x600000280910>, {2448, 3264})
image2 in memory cache is Optional(<UIImage: 0x600000280910>, {2448, 3264})
image is Optional(<UIImage: 0x600000280910>, {2448, 3264})
cacheType is memory
分析: 从结果分析来看,但你使用
Kingfisher
来存储图片时,默认是存储在memory
和disk
; 第一次run, 先是从内存中获取图片(此时内存中和磁盘中都有缓存);若我们不卸载模拟器上的app
, 然后再次运行模拟器,也即是杀死进程,那么我们看到的将是从disk
中获取图片,因为在杀死进程后,内存中的缓存被清空(回收内存),只有磁盘中有缓存; 若接着再次获取缓存图片(不杀死进程),那么我们又看到是从memory
中获取到的图片,因为第一次从disk
中获取图片后,就会将disk
中的缓存图片放在内存中进行缓存,在不杀死进程的情况下,会直接从内存中获取!!!看一下下面的再次演示:
当然,我们也可以直接指定是需要缓存到磁盘还是内存:
ImageCache.default.store(image, // 图片
original: data, // 原图片的data数据(Kingfisher推荐有,原因我后续有空会说)
forKey: "Specified_Keys", // 指定key
processorIdentifier: "", // 处理器标识符(使用处理器处理图片时,把这个标识传给它),标识会用来给 key 和 processor 的组合产生一个对应的key)
cacheSerializer: DefaultCacheSerializer.default, // 缓存序列化,这里默认
toDisk: false, // 是否缓存到disk(磁盘)
completionHandler: nil) // 完成回调
移除缓存图片
// 移除 key 为 Specified_Keys 的缓存图片,从内存和磁盘中都移除
ImageCache.default.removeImage(forKey: "Specified_Keys")
// 只清理内存中的缓存
ImageCache.default.removeImage(forKey: "Specified_Keys", processorIdentifier: "", fromDisk: false, completionHandler: nil)
限制(设置)磁盘的最大缓存(单位:字节)
// 设置磁盘的最大缓存容量为 10 M, 默认是0,表示无限制
ImageCache.default.maxDiskCacheSize = 10 * 1024 * 1024
获取(计算)当前磁盘缓存大小
ImageCache.default.calculateDiskCacheSize { (usedDiskCacheSize) in
print("usedDiskCacheSize is \(usedDiskCacheSize)")
}
手动清理缓存
- 立即清除内存缓存
ImageCache.default.clearMemoryCache()
- 清除磁盘缓存(异步操作)
ImageCache.default.clearDiskCache()
- 清除过期或超过磁盘缓存大小的缓存(异步操作)
ImageCache.default.cleanExpiredDiskCache()
注意:当你的
app
接收到内存警告(memory warning)的时候,Kingfisher
会净化内存缓存,也就是说,在需要的时候,Kingfisher
会主动清理一些已过期或者超过缓存尺寸大小的缓存,因此一般而言,没有必要自己手动去清理缓存,而这些清理缓存方法的存在主要在于以防你想要用户有更多对缓存进行操作的情况。比如有时候,有部分app
习惯在设置里面加一个清理缓存
的交互,为了方便,你可以根据需要调用这些手动清理缓存的方法。
设置存储在磁盘中的最长的缓存持续时间
// 5天后磁盘缓存将过期
ImageCache.default.maxCachePeriodInSecond = 5 * 24 * 60 * 60
注意:默认是一个周(即7天),单位是 秒。必须注意的是,如果设置成一个负值(比如 -1 )那么磁盘缓存决不会过期!
将一个默认路径扩展添加到每个缓存文件
// set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"
注意:如果你是在macOS上使用
Kingfisher
并且想要添加拖放的操作的时候特别有用,大多数web输入字段不接受没有路径扩展的文件。什么意思呢?比如,你想在一个可拖进图片进行识别的表单中拖拽一张没有后缀.jpg
或png
的格式图片进去,实际上你操作的时候表单框是拒绝你的,你无法拖进去,如果你加了图片后缀识别名jpg
等后,就可以拖进去了,这就是这个方法的莫大好处,当然,如果你是iOS客户端开发,就忽略这个功能,因为作者说了,这个实在macOS
桌面应用上特别有用。
为默认图片下载器设置超时持续时间
// 设置成30秒,默认是15秒
ImageDownloader.default.downloadTimeout = 30
好了,到此为止,对于Kingfisher的基本使用介绍差不多了,后面还有很多方法,包括自定义下载器和缓存、取消下载任务或获取任务、在发送前修改请求等等 我们将在后面陆续介绍,对于喜欢Kingfisher
的小伙伴们也可以自己深入研究,基本的使用设置事实上很简单,但尽管如此,Kingfisher
中的很多地方还是很值得一探究竟的。
欢迎加入 iOS(swift)开发互助群:QQ群号:558179558, 相互讨论和学习!