無(wú)需修改SDWebImage源代碼,實(shí)現(xiàn)SDWebImage框架下的圖片漸變加載效果

眾所周知,在SDWebImage框架下想要對(duì)圖片在下載和下載過(guò)程中的狀態(tài)進(jìn)行處理,需要使用SDWebImageOptions,我們首先看一下SDWebImage中有哪些可選項(xiàng)

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    /**
     * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
     * This flag disable this blacklisting.
     */
    SDWebImageRetryFailed = 1 << 0,

    /**
     * By default, image downloads are started during UI interactions, this flags disable this feature,
     * leading to delayed download on UIScrollView deceleration for instance.
     */
    SDWebImageLowPriority = 1 << 1,

    /**
     * This flag disables on-disk caching
     */
    SDWebImageCacheMemoryOnly = 1 << 2,

    /**
     * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
     * By default, the image is only displayed once completely downloaded.
     */
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
     * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
     * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
     * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
     *
     * Use this flag only if you can't make your URLs static with embedded cache busting parameter.
     */
    SDWebImageRefreshCached = 1 << 4,

    /**
     * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
     * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
     */
    SDWebImageContinueInBackground = 1 << 5,

    /**
     * Handles cookies stored in NSHTTPCookieStore by setting
     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
     */
    SDWebImageHandleCookies = 1 << 6,

    /**
     * Enable to allow untrusted SSL certificates.
     * Useful for testing purposes. Use with caution in production.
     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**
     * By default, images are loaded in the order in which they were queued. This flag moves them to
     * the front of the queue.
     */
    SDWebImageHighPriority = 1 << 8,
    
    /**
     * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
     * of the placeholder image until after the image has finished loading.
     */
    SDWebImageDelayPlaceholder = 1 << 9,

    /**
     * We usually don't call transformDownloadedImage delegate method on animated images,
     * as most transformation code would mangle it.
     * Use this flag to transform them anyway.
     */
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    /**
     * By default, image is added to the imageView after download. But in some cases, we want to
     * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
     * Use this flag if you want to manually set the image in the completion when success
     */
    SDWebImageAvoidAutoSetImage = 1 << 11
};

可以看出,最接近我們所需要的「漸變」效果的,就是這個(gè)SDWebImageProgressiveDownload,這確實(shí)是一個(gè)逐步顯示圖片的過(guò)程,但是這個(gè)類型所完成的效果,僅僅是一個(gè)從上到下逐漸顯示的效果,離我們需要的「漸變」效果相差甚遠(yuǎn)。所以說(shuō)的直白一些,SDWebImage這個(gè)框架目前是不支持大多數(shù)主流成熟框架均支持的圖片加載「漸變」效果的。

這樣一來(lái),要想實(shí)現(xiàn)「漸變」效果,我們似乎只剩下兩條路可以走:

1.替換庫(kù),如YYImage等

  • 優(yōu)點(diǎn):框架自帶支持,無(wú)需使用者自己處理,方便快捷省事
  • 缺點(diǎn):顯然,在開(kāi)發(fā)過(guò)程中,突然替換基礎(chǔ)庫(kù)的成本是很高的,尤其是在DeadLine為期不遠(yuǎn)的時(shí)候。如果團(tuán)隊(duì)中有人不熟悉新庫(kù),那么還會(huì)額外衍生學(xué)習(xí)時(shí)間成本,這對(duì)快速迭代來(lái)說(shuō),是很難接受的一件事

2.對(duì)SDWebImage進(jìn)行修改,手動(dòng)完成對(duì)「漸變」效果的支持

  • 優(yōu)點(diǎn):節(jié)省了更換其他庫(kù)的時(shí)間成本,降低了替換庫(kù)所造成的風(fēng)險(xiǎn)和回歸測(cè)試的成本
  • 缺點(diǎn):可能需要對(duì)源代碼進(jìn)行修改,如果是使用CocoaPods集成的,那就很麻煩了

這么對(duì)比一看,似乎每一條路的成本都不低,難道真的只剩下這兩條路可以走了嗎?

答案是: NO

我看到大多數(shù)文章對(duì)于SDWebImage實(shí)現(xiàn)漸變效果的處理方式都是修改源碼,這個(gè)做法實(shí)在是太Low了??,任何需要修改成熟開(kāi)源框架源碼才能完成的事情,都是最爛的思路和做法。SDWebImage既然能夠成為適用范圍最廣,最主流的網(wǎng)絡(luò)圖片框架之一,其對(duì)各種情況和需求的支持力度必然不可能如此局限。即使它不直接支持,那么我們?yōu)楹尾粨Q個(gè)思路和做法呢?SDWebImage提供的方法如此豐富,何必要局限于一種,下面我們就來(lái)看看如何在不修改SDWebImage源碼的情況下,來(lái)達(dá)到圖片加載的「漸變」效果,Let's go!

首先,我們不妨可以先思考一下什么是「漸變」效果:無(wú)非就是隨著圖片的加載逐漸由模糊到清晰的顯示出來(lái),這種效果完全可以通過(guò)一個(gè)簡(jiǎn)單的CATransition動(dòng)畫(huà)來(lái)完成,唯一的“難點(diǎn)”不過(guò)是這個(gè)動(dòng)畫(huà)是add和remove的時(shí)機(jī)而已,大多數(shù)此類解決方案都是直接把這個(gè)簡(jiǎn)單的動(dòng)畫(huà)效果嵌入SDWebImage源碼中,這個(gè)做法不僅Low,而且對(duì)SDWebImage源碼也帶來(lái)了傷害,最重要的是,如果你繼續(xù)用這個(gè)庫(kù),難道每次SDWebImage升級(jí)你都要再修改一遍源碼嗎,exm???

所以,通過(guò)修改SDWebImage源碼來(lái)嵌入這種動(dòng)畫(huà)的開(kāi)發(fā)者,幾乎可以肯定,全都是對(duì)CAAnimation基礎(chǔ)完全不熟悉的人。只要是對(duì)CAAnimation熟悉的開(kāi)發(fā)者都會(huì)知道,CAAnimationDelegate協(xié)議中提供了一個(gè)animationDidStop(_ anim: CAAnimation, finished flag: Bool)方法,該方法可以根據(jù)Animation的key對(duì)所有基于CAAnimation的動(dòng)畫(huà)進(jìn)行監(jiān)聽(tīng)操作。這么一來(lái),我們接下來(lái)要做什么,你是不是就很明白了呢。

我們可以來(lái)看一下SDWebImage提供的加載網(wǎng)絡(luò)圖片的方法中,是有當(dāng)圖片完成后的回調(diào)方法的,如:

/**
 * Set the imageView `image` with an `url`, placeholder and custom options.
 *
 * The download is asynchronous and cached.
 *
 * @param url            The url for the image.
 * @param placeholder    The image to be set initially, until the image request finishes.
 * @param options        The options to use when downloading the image. @see SDWebImageOptions for the possible values.
 * @param completedBlock A block called when operation has been completed. This block has no return value
 *                       and takes the requested UIImage as first parameter. In case of error the image parameter
 *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
 *                       indicating if the image was retrieved from the local cache or from the network.
 *                       The fourth parameter is the original image url.
 */
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock;

我們可以在SDWebImageCompletionBlock這個(gè)Block塊中來(lái)實(shí)現(xiàn)我們的CATransition動(dòng)畫(huà)

/*
*  @param options: 選擇.lowPriority(oc中是SDWebImageLowPriority),是為了禁止在UI交互過(guò)程中啟動(dòng)圖像下載,保證列表流暢性。例如,導(dǎo)致UIScrollView減速的下載延遲
*
*
*/
imageView.sd_setImage(with: theUrl as URL, placeholderImage: UIImage.init(named: "newVote_ placeholder"), options: .lowPriority, completed: { [weak self] (image, error, cacheType, url) in
   guard let strongSelf = self else { return }
   if cacheType == .none { // 只有當(dāng)緩存中沒(méi)有圖片,也就是首次加載時(shí)才實(shí)現(xiàn)CATransition動(dòng)畫(huà)
      let transition:CATransition = CATransition()
      transition.type = kCATransitionFade // 褪色效果,漸進(jìn)效果的基礎(chǔ)
      transition.duration = 0.85
      transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut) // 先慢后快再慢 
      strongSelf.layer.add(transition, forKey: "newVoteTimeline") // 在layer中加入動(dòng)畫(huà),并約定好該動(dòng)畫(huà)的key
   }
})

我們來(lái)解釋一下上面這段代碼究竟做了什么:

options: 選擇.lowPriority(oc中是SDWebImageLowPriority),是為了禁止在UI交互過(guò)程中啟動(dòng)圖像下載,保證列表流暢性。例如,導(dǎo)致UIScrollView減速的下載延遲

判斷cacheType == .none: 只有當(dāng)緩存中沒(méi)有圖片,也就是首次加載時(shí)才實(shí)現(xiàn)CATransition動(dòng)畫(huà),因?yàn)镾DWebImage會(huì)根據(jù)url從緩存中查找是否存在相同的圖片,只有當(dāng)緩存中不存在該圖片時(shí)才會(huì)啟動(dòng)下載,同理,我們的「漸變」效果也是只有當(dāng)啟動(dòng)下載是才會(huì)發(fā)生,否則,在列表中復(fù)用時(shí)會(huì)持續(xù)顯示「漸變」效果,無(wú)論是否已經(jīng)下載完成

然后,我們遵循CAAnimationDelegate協(xié)議,并實(shí)現(xiàn)animationDidStop(_ anim: CAAnimation, finished flag: Bool)方法:

// MARK: 監(jiān)聽(tīng)漸變動(dòng)畫(huà)是否已完成
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
   if flag {
      self.layer.removeAnimation(forKey: "newVoteTimeline") // 當(dāng)newVoteTimeline動(dòng)畫(huà)已經(jīng)完成,將其從layer中移除,避免復(fù)用中的產(chǎn)生反復(fù)「漸變」效果
   }
}

以上就完成了針對(duì)SDWebImage框架的「漸變」效果修改,幾行代碼即可搞定,完全不需要對(duì)SDWebImage進(jìn)行內(nèi)部修改,有時(shí)候換一種思路思考問(wèn)題,世界會(huì)豁然開(kāi)朗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,835評(píng)論 4 61
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評(píng)論 25 708
  • ??IDE:Android Studio??無(wú)法使用Recyclerview,android.support.v7...
    arousalzk閱讀 4,970評(píng)論 0 0
  • 后臺(tái)數(shù)據(jù)管理 后臺(tái)采用的Parse.com,真的好好用!而且guide寫(xiě)得超級(jí)好,連界面都模仿的蘋(píng)果的風(fēng)格,行文清...
    _lemon閱讀 544評(píng)論 0 0
  • 炎炎夏日,接到七歲侄女打來(lái)的電話約我去游泳。侄女住的小區(qū)的樓下有個(gè)室外的游泳池,很方便。酷暑難耐,我欣然答...
    靈清的玉閱讀 761評(píng)論 0 5

友情鏈接更多精彩內(nèi)容