SDWebImage 加載顯示 GIF 與性能問題

支持 GIF 的圖片組件 BBWebImagehtml

SDWebImage 加載顯示 GIF 與性能問題

SDWebImage 4.0 以前,能夠用 UIImageView 顯示 GIF 圖。若是 SDWebImage 4.0 還這麼作,只會顯示靜態圖。SDWebImage 4.0 用 FLAnimatedImageView 經過 FLAnimatedImage 顯示 GIF 圖。本文的這兩個庫的版本分別爲 SDWebImage 4.0.0 和 FLAnimatedImage 1.0.12。git

CocoaPods 安裝

pod 'SDWebImage'
pod 'SDWebImage/GIF'

通常用法

用 FLAnimatedImageView 代替 UIImageView,顯示 GIF。FLAnimatedImage 的 README.md 中介紹的用法github

FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"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];

千萬別這麼寫,這段代碼會阻塞主線程!在主線程經過 URL 獲取 NSData,等下載結束才執行下一步。web

FLAnimatedImageView 的用法和 UIImageView 類似,初始化、設置 frame、添加到視圖上、用 UIImage 給 image 屬性賦值顯示靜態圖片;不同的是,用 FLAnimatedImage 給 animatedImage 屬性賦值顯示動態圖片。以上代碼的問題在於 FLAnimatedImage 的生成部分。swift

SDWebImage 給 FLAnimatedImageView 添加了異步加載 GIF 的方法,與異步加載靜態圖片同樣異步

imageView.sd_setImage(with: url, placeholderImage: placeholder)

若是顯示少許的 GIF,這樣寫應該能夠。然而,若是須要用 UITableView 或 UICollectionView 展現大量 GIF,這麼寫可能會有性能問題,滑動時發生頓卡。async

提高性能

爲了提升性能,能夠指定 RunLoopMode,在 default mode 進行動畫,在 tracking mode (好比 scroll view 滑動時) 中止動畫oop

imageView.runLoopMode = RunLoopMode.defaultRunLoopMode.rawValue

個人代碼中,這麼寫仍是會有頓卡。查看 SDWebImage 的源碼,發現了問題。性能

sd_setImage(with:placeholderImage:) 會調用 sd_internalSetImageWithURL: 方法動畫

注意,sd_internalSetImageWithURL: 方法中的 setImageBlock 參數,在今生成 FLAnimatedImage。進一步查看 sd_internalSetImageWithURL: 方法的實現

宏定義 dispatch_main_async_safe(block) 保證 block 在主線程中執行,其中包含 setImageBlock。所以 setImageBlock 在主線程中執行,也就是說 FLAnimatedImage 在主線程中生成,這一步比較耗時,阻塞主線程,形成頓卡。

解決辦法是,把 FLAnimatedImage 的生成放到子線程中。能夠直接修改 SDWebImage 的源碼,但不建議這麼作。比較好的辦法是,給 FLAnimatedImageView 添加方法

extension FLAnimatedImageView {
    
    func setImage(with url: URL?, placeholderImage: UIImage?) {
        sd_internalSetImage(with: url, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), operationKey: nil, setImageBlock: { [weak self] (image, imageData) in
            guard let strongSelf = self else { return }
            
            let imageFormat = NSData.sd_imageFormat(forImageData: imageData)
            if imageFormat == .GIF {
                // Enter global queue
                DispatchQueue.global(qos: .userInteractive).async { [weak self] in
                    // Create FLAnimatedImage in global queue
                    let animatedImage = FLAnimatedImage(animatedGIFData: imageData)
                    DispatchQueue.main.async { [weak self] in
                        guard let strongSelf = self else { return }
                        // Set image in main queue
                        strongSelf.animatedImage = animatedImage
                        strongSelf.image = nil
                    }
                }
            } else {
                // Set image in main queue
                strongSelf.image = image
                strongSelf.animatedImage = nil
            }
            }, progress: nil, completed: nil)
    }
}

一樣調用 sd_internalSetImageWithURL: 方法,只是修改 setImageBlock 參數,在子線程中建立 FLAnimatedImage,而後在主線程中設置圖片。

這個方法也適用於靜態圖片。若是圖片是靜態圖片,直接在主線程中設置圖片,不用進入子線程。

調用這個方法很簡單

imageView.setImage(with: url, placeholderImage: placeholder)

這樣寫,UITableView 滑動就很流暢了。

代碼已上傳 GitHub:https://github.com/Silence-GitHub/GIFDemo

這個解決方案雖然能用,可是從設計的角度看,SDWebImage 使用 FLAnimatedImage 並不合適。有興趣能夠試試支持 GIF 的圖片組件 BBWebImage

轉載請註明出處:http://www.cnblogs.com/silence-cnblogs/p/6682867.html

相關文章
相關標籤/搜索