序言
Kingfisher 是一個下載撬码、緩存網(wǎng)絡(luò)圖片的輕量級純swift庫, 作者@王巍自稱是受著名三方庫SDWebImage激勵所寫版保,一年多以來呜笑,該庫深受廣大iOS之swift開發(fā)者所喜愛,目前被很多iOS開發(fā)者應(yīng)用在app中彻犁。在swift中它真的是一個SDWebImage的升級版叫胁,作為swift開發(fā)者來說,為了摒棄Objective-C的風(fēng)格汞幢,甚至“斷絕”與Objective-C的關(guān)系曹抬,使工程更swift化,我們更希望更喜歡使用純凈的swift來開發(fā)自己的app急鳄。在此,也非常感謝@喵神給眾多開發(fā)者提供了很大的便利堰酿,
為業(yè)界開發(fā)者所做的無私貢獻(xiàn)疾宏。
github: [Kingfisher] (https://github.com/onevcat/Kingfisher)
特征
- 異步下載和緩存圖片
- 基于
networking
的URLSession
, 提供基礎(chǔ)的圖片處理器和過濾器 - 內(nèi)存和磁盤的多層緩存
- 可撤銷組件,可根據(jù)需要分開地使用下載器和緩存系統(tǒng)
- 必要時可從緩存中讀取并展示圖片
- 擴(kuò)展
UIImageView
触创、NSImage
坎藐、UIButton
來直接設(shè)置一個URL
圖片 - 設(shè)置圖片時,內(nèi)置過渡動畫
- 支持?jǐn)U展圖片處理和圖片格式
先看一個Kingfisher的基本用法:從網(wǎng)絡(luò)上設(shè)置一張基本的圖片
let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg")
imageView.kf.setImage(with: url)
美女躍然屏上(模擬器截圖)
Kingfisher從
url
下載圖片,將圖片存儲在內(nèi)存緩存和磁盤緩存岩馍,然后將它展示在imageView
上碉咆。當(dāng)使用同樣的代碼后(url不變),就會直接從內(nèi)存中獲取之前緩存的圖片并立即顯示出來蛀恩。
要求
- iOS 8.0+ / macOS 10.10+
- Swift 3(Kingfisher 3.x), Swift 2.3(Kingfisher 2.x)
Kingfisher 3.0遷移指導(dǎo) : 從早期版本升級到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
基本使用
直接設(shè)置一張url圖片
if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
imgView.kf.setImage(with: url)
}
詮釋:使用直接設(shè)置的方法疫铜,
Kingfisher
首先會嘗試從緩存中去取,如果沒有双谆,則直接下載圖片并且緩存下來以備后用壳咕。此外,Kingfisher
默認(rèn)使用absoluteString of url
(即絕對url)作為cacheKey
以方便再次加載該圖片的時候去緩存中根據(jù)cacheKey(也就是圖片url)
查找顽馋,通俗來說就是把圖片的整個鏈接作為cacheKey
來緩存在本地谓厘。
指定一個cacheKey
緩存圖片
let imageResource = ImageResource(downloadURL: url, cacheKey: "Custom_cache_key")
imageView.kf.setImage(with: imageResource)
設(shè)置占位符圖片
在Kingfisher
中,為了防止加載網(wǎng)絡(luò)圖片失敗寸谜,提供了一個設(shè)置占位符圖片功能竟稳,也就是說,當(dāng)網(wǎng)絡(luò)加載過程中或者圖片加載失敗時熊痴,就使用自定義的默認(rèn)圖片來代替顯示他爸。
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
提供了一個在下載圖片過程中顯示加載菊花的功能,圖片加載成功后菊花自動消失惜论,可以通過設(shè)置indicatorType
來顯示菊花
IndicatorType
是一個枚舉
public enum IndicatorType {
/// 默認(rèn)沒有菊花
case none
/// 使用系統(tǒng)菊花
case activity
/// 使用一張圖片作為菊花许赃,支持gif圖
case image(imageData: Data)
/// 使用自定義菊花,要遵循Indicator協(xié)議
case custom(indicator: Indicator)
}
設(shè)置方式 1 : 使用系統(tǒng)菊花
imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: url)
演示效果(加載的是我微博上的一張圖片)
設(shè)置方式 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)
演示效果
設(shè)置方式 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)為我們提供了默認(rèn)實現(xiàn)(我還因為這個問題在github上問過他沟于,突然覺得好蠢...)
設(shè)置方式 4 : 根據(jù)實時下載圖片的數(shù)據(jù)做進(jìn)度條加載或者菊花加載(靈活咳胃,比例為:圖片已下載數(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)%")
// 這里用進(jìn)度條或者繪制view都可以,然后根據(jù) percentage% 表示進(jìn)度就行了
}, completionHandler: nil)
設(shè)置過渡動畫旷太,漸變效果(圖片下載結(jié)束后)
imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(0.4))], progressBlock: nil, completionHandler: nil)
詮釋: 在
Kingfisher
中展懈,有一個options
, 我們可以做一些選擇销睁,Kingfisher里面目前包含了18種選擇,在圖片下載結(jié)束后存崖,為了實現(xiàn)一些效果冻记,我們可以實現(xiàn)上面的漸變效果,或者還有更多的翻滾效果来惧! 但是Kingfisher
冗栗,上面的這個過渡動畫值只是針對圖片是從web下載時調(diào)用,如果是從內(nèi)存或磁盤中取時是不會有這個效果的违寞,雖然里面也有一個枚舉值強(qiáng)制動畫forceTransition
,但是似乎暫時作者還沒有實現(xiàn)這個功能贞瞒。
設(shè)置圖片模糊效果
let processor = BlurImageProcessor(blurRadius: 25)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)], progressBlock: nil, completionHandler: nil)
詮釋:還有很多其他選擇,可自行根據(jù)需要設(shè)置趁曼。
強(qiáng)制下載(跳過緩存军浆,直接從web下載圖片)
imageView.kf.setImage(with: url, placeholder: nil, options: [.forceRefresh], progressBlock: nil, completionHandler: nil)
強(qiáng)制從緩存中獲取圖片(緩存中沒有也不會從網(wǎng)絡(luò)下載)
imageView.kf.setImage(with: url, placeholder: nil, options: [.onlyFromCache], progressBlock: nil, completionHandler: nil)
給按鈕(UIButton)設(shè)置圖片或背景圖片
同設(shè)置UIImageView
一樣, 我們也可以使用Kingfisher
來給UIButton
設(shè)置圖片, 用法相同:
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")
獲取圖片:
// 獲取緩存檢查結(jié)果
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
}
演示效果:
打印結(jié)果:
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
分析: 從結(jié)果分析來看摄悯,但你使用
Kingfisher
來存儲圖片時赞季,默認(rèn)是存儲在memory
和disk
; 第一次run奢驯, 先是從內(nèi)存中獲取圖片(此時內(nèi)存中和磁盤中都有緩存)申钩;若我們不卸載模擬器上的app
, 然后再次運行模擬器,也即是殺死進(jìn)程瘪阁,那么我們看到的將是從disk
中獲取圖片撒遣,因為在殺死進(jìn)程后,內(nèi)存中的緩存被清空(回收內(nèi)存)管跺,只有磁盤中有緩存; 若接著再次獲取緩存圖片(不殺死進(jìn)程)义黎,那么我們又看到是從memory
中獲取到的圖片,因為第一次從disk
中獲取圖片后豁跑,就會將disk
中的緩存圖片放在內(nèi)存中進(jìn)行緩存廉涕,在不殺死進(jìn)程的情況下,會直接從內(nèi)存中獲韧摹:伞!卸夕!看一下下面的再次演示:
當(dāng)然层释,我們也可以直接指定是需要緩存到磁盤還是內(nèi)存:
ImageCache.default.store(image, // 圖片
original: data, // 原圖片的data數(shù)據(jù)(Kingfisher推薦有,原因我后續(xù)有空會說)
forKey: "Specified_Keys", // 指定key
processorIdentifier: "", // 處理器標(biāo)識符(使用處理器處理圖片時娇哆,把這個標(biāo)識傳給它),標(biāo)識會用來給 key 和 processor 的組合產(chǎn)生一個對應(yīng)的key)
cacheSerializer: DefaultCacheSerializer.default, // 緩存序列化,這里默認(rèn)
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)
限制(設(shè)置)磁盤的最大緩存(單位:字節(jié))
// 設(shè)置磁盤的最大緩存容量為 10 M, 默認(rèn)是0碍讨,表示無限制
ImageCache.default.maxDiskCacheSize = 10 * 1024 * 1024
獲戎瘟Α(計算)當(dāng)前磁盤緩存大小
ImageCache.default.calculateDiskCacheSize { (usedDiskCacheSize) in
print("usedDiskCacheSize is \(usedDiskCacheSize)")
}
手動清理緩存
- 立即清除內(nèi)存緩存
ImageCache.default.clearMemoryCache()
- 清除磁盤緩存(異步操作)
ImageCache.default.clearDiskCache()
- 清除過期或超過磁盤緩存大小的緩存(異步操作)
ImageCache.default.cleanExpiredDiskCache()
注意:當(dāng)你的
app
接收到內(nèi)存警告(memory warning)的時候,Kingfisher
會凈化內(nèi)存緩存勃黍,也就是說宵统,在需要的時候,Kingfisher
會主動清理一些已過期或者超過緩存尺寸大小的緩存覆获,因此一般而言马澈,沒有必要自己手動去清理緩存,而這些清理緩存方法的存在主要在于以防你想要用戶有更多對緩存進(jìn)行操作的情況弄息。比如有時候痊班,有部分app
習(xí)慣在設(shè)置里面加一個清理緩存
的交互,為了方便摹量,你可以根據(jù)需要調(diào)用這些手動清理緩存的方法涤伐。
設(shè)置存儲在磁盤中的最長的緩存持續(xù)時間
// 5天后磁盤緩存將過期
ImageCache.default.maxCachePeriodInSecond = 5 * 24 * 60 * 60
注意:默認(rèn)是一個周(即7天),單位是 秒缨称。必須注意的是凝果,如果設(shè)置成一個負(fù)值(比如 -1 )那么磁盤緩存決不會過期!
將一個默認(rèn)路徑擴(kuò)展添加到每個緩存文件
// set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"
注意:如果你是在macOS上使用
Kingfisher
并且想要添加拖放的操作的時候特別有用睦尽,大多數(shù)web輸入字段不接受沒有路徑擴(kuò)展的文件器净。什么意思呢?比如当凡,你想在一個可拖進(jìn)圖片進(jìn)行識別的表單中拖拽一張沒有后綴.jpg
或png
的格式圖片進(jìn)去山害,實際上你操作的時候表單框是拒絕你的,你無法拖進(jìn)去宁玫,如果你加了圖片后綴識別名jpg
等后粗恢,就可以拖進(jìn)去了,這就是這個方法的莫大好處欧瘪,當(dāng)然眷射,如果你是iOS客戶端開發(fā),就忽略這個功能佛掖,因為作者說了妖碉,這個實在macOS
桌面應(yīng)用上特別有用。
為默認(rèn)圖片下載器設(shè)置超時持續(xù)時間
// 設(shè)置成30秒芥被,默認(rèn)是15秒
ImageDownloader.default.downloadTimeout = 30
好了欧宜,到此為止,對于Kingfisher的基本使用介紹差不多了拴魄,后面還有很多方法冗茸,包括自定義下載器和緩存席镀、取消下載任務(wù)或獲取任務(wù)、在發(fā)送前修改請求等等 我們將在后面陸續(xù)介紹夏漱,對于喜歡Kingfisher
的小伙伴們也可以自己深入研究豪诲,基本的使用設(shè)置事實上很簡單,但盡管如此挂绰,Kingfisher
中的很多地方還是很值得一探究竟的屎篱。
歡迎加入 iOS(swift)開發(fā)互助群:QQ群號:558179558, 相互討論和學(xué)習(xí)葵蒂!