序言
Kingfisher 是一個下載、緩存網(wǎng)絡圖片的輕量級純swift庫, 作者@王巍自稱是受著名三方庫SDWebImage激勵所寫,一年多以來,該庫深受廣大iOS之swift開發(fā)者所喜愛,目前被很多iOS開發(fā)者應用在app中。在swift中它真的是一個SDWebImage的升級版,作為swift開發(fā)者來說,為了摒棄Objective-C的風格,甚至“斷絕”與Objective-C的關系,使工程更swift化,我們更希望更喜歡使用純凈的swift來開發(fā)自己的app。在此,也非常感謝@喵神給眾多開發(fā)者提供了很大的便利,
為業(yè)界開發(fā)者所做的無私貢獻。
github: [Kingfisher] (https://github.com/onevcat/Kingfisher)
特征
- 異步下載和緩存圖片
- 基于
networking的URLSession, 提供基礎的圖片處理器和過濾器 - 內(nèi)存和磁盤的多層緩存
- 可撤銷組件,可根據(jù)需要分開地使用下載器和緩存系統(tǒng)
- 必要時可從緩存中讀取并展示圖片
- 擴展
UIImageView、NSImage、UIButton來直接設置一個URL圖片 - 設置圖片時,內(nèi)置過渡動畫
- 支持擴展圖片處理和圖片格式
先看一個Kingfisher的基本用法:從網(wǎng)絡上設置一張基本的圖片
let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg")
imageView.kf.setImage(with: url)
美女躍然屏上(模擬器截圖)

Kingfisher從
url下載圖片,將圖片存儲在內(nèi)存緩存和磁盤緩存,然后將它展示在imageView上。當使用同樣的代碼后(url不變),就會直接從內(nèi)存中獲取之前緩存的圖片并立即顯示出來。
要求
- 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以方便再次加載該圖片的時候去緩存中根據(jù)cacheKey(也就是圖片url)查找,通俗來說就是把圖片的整個鏈接作為cacheKey來緩存在本地。
指定一個cacheKey緩存圖片
let imageResource = ImageResource(downloadURL: url, cacheKey: "Custom_cache_key")
imageView.kf.setImage(with: imageResource)
設置占位符圖片
在Kingfisher中,為了防止加載網(wǎng)絡圖片失敗,提供了一個設置占位符圖片功能,也就是說,當網(wǎng)絡加載過程中或者圖片加載失敗時,就使用自定義的默認圖片來代替顯示。
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)
}
下載完成回調(diào)
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 從內(nèi)存中獲取到的緩存圖片(第二次及以上加載)
// .disk 從磁盤中獲取到的緩存圖片(第二次及以上加載)
imageUrl // 所要下載的圖片的url
})
}
加載菊花
Kingfisher提供了一個在下載圖片過程中顯示加載菊花的功能,圖片加載成功后菊花自動消失,可以通過設置indicatorType來顯示菊花
IndicatorType是一個枚舉
public enum IndicatorType {
/// 默認沒有菊花
case none
/// 使用系統(tǒng)菊花
case activity
/// 使用一張圖片作為菊花,支持gif圖
case image(imageData: Data)
/// 使用自定義菊花,要遵循Indicator協(xié)議
case custom(indicator: Indicator)
}
設置方式 1 : 使用系統(tǒng)菊花
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協(xié)議
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是一個協(xié)議,如果我們要實現(xiàn)自定義菊花加載,只需要遵循這個協(xié)議即可,另外必須得說的是,
Indicator協(xié)議中有一個屬性viewCenter我們不需要在遵循協(xié)議的時候去實現(xiàn),因為喵神已經(jīng)為我們提供了默認實現(xiàn)(我還因為這個問題在github上問過他,突然覺得好蠢...)
設置方式 4 : 根據(jù)實時下載圖片的數(shù)據(jù)做進度條加載或者菊花加載(靈活,比例為:圖片已下載數(shù)據(jù) / 圖片總數(shù)據(jù))
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都可以,然后根據(jù) percentage% 表示進度就行了
}, completionHandler: nil)
設置過渡動畫,漸變效果(圖片下載結束后)
imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(0.4))], progressBlock: nil, completionHandler: nil)
詮釋: 在
Kingfisher中,有一個options, 我們可以做一些選擇,Kingfisher里面目前包含了18種選擇,在圖片下載結束后,為了實現(xiàn)一些效果,我們可以實現(xiàn)上面的漸變效果,或者還有更多的翻滾效果! 但是Kingfisher,上面的這個過渡動畫值只是針對圖片是從web下載時調(diào)用,如果是從內(nèi)存或磁盤中取時是不會有這個效果的,雖然里面也有一個枚舉值強制動畫forceTransition,但是似乎暫時作者還沒有實現(xiàn)這個功能。
設置圖片模糊效果
let processor = BlurImageProcessor(blurRadius: 25)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)], progressBlock: nil, completionHandler: nil)

詮釋:還有很多其他選擇,可自行根據(jù)需要設置。
強制下載(跳過緩存,直接從web下載圖片)
imageView.kf.setImage(with: url, placeholder: nil, options: [.forceRefresh], progressBlock: nil, completionHandler: nil)
強制從緩存中獲取圖片(緩存中沒有也不會從網(wǎng)絡下載)
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 // 已接收圖片數(shù)據(jù)
totalData // 圖片總大小
}, completionHandler: { (image, error, url, data) in
image // 圖片
error // 下載失敗時的錯誤信息
url // 所要下載的圖片的url
data // 下載完成后的總數(shù)據(jù)
})
- 使用
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))")
// 從內(nèi)存獲取圖片
let image1 = ImageCache.default.retrieveImageInMemoryCache(forKey: "Specified_Keys")
print("image1 in memory cache is \(String(describing: image1))")
// 從內(nèi)存獲取圖片
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, 先是從內(nèi)存中獲取圖片(此時內(nèi)存中和磁盤中都有緩存);若我們不卸載模擬器上的app, 然后再次運行模擬器,也即是殺死進程,那么我們看到的將是從disk中獲取圖片,因為在殺死進程后,內(nèi)存中的緩存被清空(回收內(nèi)存),只有磁盤中有緩存; 若接著再次獲取緩存圖片(不殺死進程),那么我們又看到是從memory中獲取到的圖片,因為第一次從disk中獲取圖片后,就會將disk中的緩存圖片放在內(nèi)存中進行緩存,在不殺死進程的情況下,會直接從內(nèi)存中獲取?。?!看一下下面的再次演示:

當然,我們也可以直接指定是需要緩存到磁盤還是內(nèi)存:
ImageCache.default.store(image, // 圖片
original: data, // 原圖片的data數(shù)據(jù)(Kingfisher推薦有,原因我后續(xù)有空會說)
forKey: "Specified_Keys", // 指定key
processorIdentifier: "", // 處理器標識符(使用處理器處理圖片時,把這個標識傳給它),標識會用來給 key 和 processor 的組合產(chǎn)生一個對應的key)
cacheSerializer: DefaultCacheSerializer.default, // 緩存序列化,這里默認
toDisk: false, // 是否緩存到disk(磁盤)
completionHandler: nil) // 完成回調(diào)
移除緩存圖片
// 移除 key 為 Specified_Keys 的緩存圖片,從內(nèi)存和磁盤中都移除
ImageCache.default.removeImage(forKey: "Specified_Keys")
// 只清理內(nèi)存中的緩存
ImageCache.default.removeImage(forKey: "Specified_Keys", processorIdentifier: "", fromDisk: false, completionHandler: nil)
限制(設置)磁盤的最大緩存(單位:字節(jié))
// 設置磁盤的最大緩存容量為 10 M, 默認是0,表示無限制
ImageCache.default.maxDiskCacheSize = 10 * 1024 * 1024
獲取(計算)當前磁盤緩存大小
ImageCache.default.calculateDiskCacheSize { (usedDiskCacheSize) in
print("usedDiskCacheSize is \(usedDiskCacheSize)")
}
手動清理緩存
- 立即清除內(nèi)存緩存
ImageCache.default.clearMemoryCache()
- 清除磁盤緩存(異步操作)
ImageCache.default.clearDiskCache()
- 清除過期或超過磁盤緩存大小的緩存(異步操作)
ImageCache.default.cleanExpiredDiskCache()
注意:當你的
app接收到內(nèi)存警告(memory warning)的時候,Kingfisher會凈化內(nèi)存緩存,也就是說,在需要的時候,Kingfisher會主動清理一些已過期或者超過緩存尺寸大小的緩存,因此一般而言,沒有必要自己手動去清理緩存,而這些清理緩存方法的存在主要在于以防你想要用戶有更多對緩存進行操作的情況。比如有時候,有部分app習慣在設置里面加一個清理緩存的交互,為了方便,你可以根據(jù)需要調(diào)用這些手動清理緩存的方法。
設置存儲在磁盤中的最長的緩存持續(xù)時間
// 5天后磁盤緩存將過期
ImageCache.default.maxCachePeriodInSecond = 5 * 24 * 60 * 60
注意:默認是一個周(即7天),單位是 秒。必須注意的是,如果設置成一個負值(比如 -1 )那么磁盤緩存決不會過期!
將一個默認路徑擴展添加到每個緩存文件
// set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"
注意:如果你是在macOS上使用
Kingfisher并且想要添加拖放的操作的時候特別有用,大多數(shù)web輸入字段不接受沒有路徑擴展的文件。什么意思呢?比如,你想在一個可拖進圖片進行識別的表單中拖拽一張沒有后綴.jpg或png的格式圖片進去,實際上你操作的時候表單框是拒絕你的,你無法拖進去,如果你加了圖片后綴識別名jpg等后,就可以拖進去了,這就是這個方法的莫大好處,當然,如果你是iOS客戶端開發(fā),就忽略這個功能,因為作者說了,這個實在macOS桌面應用上特別有用。
為默認圖片下載器設置超時持續(xù)時間
// 設置成30秒,默認是15秒
ImageDownloader.default.downloadTimeout = 30
好了,到此為止,對于Kingfisher的基本使用介紹差不多了,后面還有很多方法,包括自定義下載器和緩存、取消下載任務或獲取任務、在發(fā)送前修改請求等等 我們將在后面陸續(xù)介紹,對于喜歡Kingfisher的小伙伴們也可以自己深入研究,基本的使用設置事實上很簡單,但盡管如此,Kingfisher中的很多地方還是很值得一探究竟的。
轉(zhuǎn)載:Swift中的圖片處理庫Kingfisher