SDWebImage 加載顯示 GIF 與性能問題
SDWebImage 4.0 之前斋配,可以用 UIImageView 顯示 GIF 圖雷厂。如果 SDWebImage 4.0 還這么做杂拨,只會(huì)顯示靜態(tài)圖锡凝。SDWebImage 4.0 用 FLAnimatedImageView 通過 FLAnimatedImage 顯示 GIF 圖杉女。本文的這兩個(gè)庫的版本分別為 SDWebImage 4.0.0 和 FLAnimatedImage 1.0.12贰剥。
CocoaPods 安裝
pod'SDWebImage'pod'SDWebImage/GIF'
一般用法
用 FLAnimatedImageView 代替 UIImageView倾剿,顯示 GIF。FLAnimatedImage 的 README.md 中介紹的用法
FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSDatadataWithContentsOfURL:[NSURLURLWithString:@"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
imageView.animatedImage = image;imageView.frame =CGRectMake(0.0,0.0,100.0,100.0);[self.view addSubview:imageView];
千萬別這么寫蚌成,這段代碼會(huì)阻塞主線程前痘!在主線程通過 URL 獲取 NSData,等下載結(jié)束才執(zhí)行下一步担忧。
FLAnimatedImageView 的用法和 UIImageView 相似芹缔,初始化、設(shè)置 frame瓶盛、添加到視圖上最欠、用 UIImage 給 image 屬性賦值顯示靜態(tài)圖片;不一樣的是惩猫,用 FLAnimatedImage 給 animatedImage 屬性賦值顯示動(dòng)態(tài)圖片芝硬。以上代碼的問題在于 FLAnimatedImage 的生成部分。
SDWebImage 給 FLAnimatedImageView 添加了異步加載 GIF 的方法轧房,與異步加載靜態(tài)圖片一樣
imageView.sd_setImage(with: url, placeholderImage: placeholder)
如果顯示少量的 GIF拌阴,這樣寫應(yīng)該可以。然而奶镶,如果需要用 UITableView 或 UICollectionView 展示大量 GIF迟赃,這么寫可能會(huì)有性能問題,滑動(dòng)時(shí)發(fā)生頓卡厂镇。
提升性能
為了提高性能纤壁,可以指定 RunLoopMode,在 default mode 進(jìn)行動(dòng)畫捺信,在 tracking mode (比如 scroll view 滑動(dòng)時(shí)) 停止動(dòng)畫
imageView.runLoopMode =RunLoopMode.defaultRunLoopMode.rawValue
我的代碼中酌媒,這么寫還是會(huì)有頓卡。查看 SDWebImage 的源碼残黑,發(fā)現(xiàn)了問題馍佑。
sd_setImage(with:placeholderImage:) 會(huì)調(diào)用 sd_internalSetImageWithURL: 方法
注意,sd_internalSetImageWithURL: 方法中的 setImageBlock 參數(shù)梨水,在此生成 FLAnimatedImage拭荤。進(jìn)一步查看 sd_internalSetImageWithURL: 方法的實(shí)現(xiàn)
宏定義 dispatch_main_async_safe(block) 保證 block 在主線程中執(zhí)行,其中包含 setImageBlock疫诽。因此 setImageBlock 在主線程中執(zhí)行舅世,也就是說 FLAnimatedImage 在主線程中生成旦委,這一步比較耗時(shí),阻塞主線程雏亚,造成頓卡缨硝。
解決辦法是,把 FLAnimatedImage 的生成放到子線程中罢低〔楸纾可以直接修改 SDWebImage 的源碼,但不建議這么做网持。比較好的辦法是宜岛,給 FLAnimatedImageView 添加方法
extension FLAnimatedImageView{
funcsetImage(with url: URL?, placeholderImage: UIImage?){? ? ? ? sd_internalSetImage(with: url, placeholderImage: placeholderImage, options:SDWebImageOptions(rawValue:0), operationKey:nil, setImageBlock: { [weakself] (image, imageData)inguardletstrongSelf =selfelse{return}letimageFormat =NSData.sd_imageFormat(forImageData: imageData)ifimageFormat == .GIF{// Enter global queueDispatchQueue.global(qos: .userInteractive).async { [weakself]in// Create FLAnimatedImage in global queueletanimatedImage =FLAnimatedImage(animatedGIFData: imageData)DispatchQueue.main.async { [weakself]inguardletstrongSelf =selfelse{return}// Set image in main queuestrongSelf.animatedImage = animatedImage? ? ? ? ? ? ? ? ? ? ? ? strongSelf.image =nil}? ? ? ? ? ? ? ? }? ? ? ? ? ? }else{// Set image in main queuestrongSelf.image = image? ? ? ? ? ? ? ? strongSelf.animatedImage =nil}? ? ? ? ? ? }, progress:nil, completed:nil)? ? }}
同樣調(diào)用 sd_internalSetImageWithURL: 方法,只是修改 setImageBlock 參數(shù)功舀,在子線程中創(chuàng)建 FLAnimatedImage萍倡,然后在主線程中設(shè)置圖片。
這個(gè)方法也適用于靜態(tài)圖片辟汰。如果圖片是靜態(tài)圖片列敲,直接在主線程中設(shè)置圖片,不用進(jìn)入子線程帖汞。
調(diào)用這個(gè)方法很簡單
imageView.setImage(with: url, placeholderImage: placeholder)