无需修改SDWebImage源代码,实现SDWebImage框架下的图片渐变加载效果

众所周知,在SDWebImage框架下想要对图片在下载和下载过程中的状态进行处理,需要使用SDWebImageOptions,我们首先看一下SDWebImage中有哪些可选项

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
};

可以看出,最接近我们所需要的「渐变」效果的,就是这个SDWebImageProgressiveDownload,这确实是一个逐步显示图片的过程,但是这个类型所完成的效果,仅仅是一个从上到下逐渐显示的效果,离我们需要的「渐变」效果相差甚远。所以说的直白一些,SDWebImage这个框架目前是不支持大多数主流成熟框架均支持的图片加载「渐变」效果的。

这样一来,要想实现「渐变」效果,我们似乎只剩下两条路可以走:

1.替换库,如YYImage等

  • 优点:框架自带支持,无需使用者自己处理,方便快捷省事
  • 缺点:显然,在开发过程中,突然替换基础库的成本是很高的,尤其是在DeadLine为期不远的时候。如果团队中有人不熟悉新库,那么还会额外衍生学习时间成本,这对快速迭代来说,是很难接受的一件事

2.对SDWebImage进行修改,手动完成对「渐变」效果的支持

  • 优点:节省了更换其他库的时间成本,降低了替换库所造成的风险和回归测试的成本
  • 缺点:可能需要对源代码进行修改,如果是使用CocoaPods集成的,那就很麻烦了

这么对比一看,似乎每一条路的成本都不低,难道真的只剩下这两条路可以走了吗?

答案是: NO

我看到大多数文章对于SDWebImage实现渐变效果的处理方式都是修改源码,这个做法实在是太Low了👎,任何需要修改成熟开源框架源码才能完成的事情,都是最烂的思路和做法。SDWebImage既然能够成为适用范围最广,最主流的网络图片框架之一,其对各种情况和需求的支持力度必然不可能如此局限。即使它不直接支持,那么我们为何不换个思路和做法呢?SDWebImage提供的方法如此丰富,何必要局限于一种,下面我们就来看看如何在不修改SDWebImage源码的情况下,来达到图片加载的「渐变」效果,Let's go!

首先,我们不妨可以先思考一下什么是「渐变」效果:无非就是随着图片的加载逐渐由模糊到清晰的显示出来,这种效果完全可以通过一个简单的CATransition动画来完成,唯一的“难点”不过是这个动画是add和remove的时机而已,大多数此类解决方案都是直接把这个简单的动画效果嵌入SDWebImage源码中,这个做法不仅Low,而且对SDWebImage源码也带来了伤害,最重要的是,如果你继续用这个库,难道每次SDWebImage升级你都要再修改一遍源码吗,exm???

所以,通过修改SDWebImage源码来嵌入这种动画的开发者,几乎可以肯定,全都是对CAAnimation基础完全不熟悉的人。只要是对CAAnimation熟悉的开发者都会知道,CAAnimationDelegate协议中提供了一个animationDidStop(_ anim: CAAnimation, finished flag: Bool)方法,该方法可以根据Animation的key对所有基于CAAnimation的动画进行监听操作。这么一来,我们接下来要做什么,你是不是就很明白了呢。

我们可以来看一下SDWebImage提供的加载网络图片的方法中,是有当图片完成后的回调方法的,如:

/**
 * 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这个Block块中来实现我们的CATransition动画

/*
*  @param options: 选择.lowPriority(oc中是SDWebImageLowPriority),是为了禁止在UI交互过程中启动图像下载,保证列表流畅性。例如,导致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 { // 只有当缓存中没有图片,也就是首次加载时才实现CATransition动画
      let transition:CATransition = CATransition()
      transition.type = kCATransitionFade // 褪色效果,渐进效果的基础
      transition.duration = 0.85
      transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut) // 先慢后快再慢 
      strongSelf.layer.add(transition, forKey: "newVoteTimeline") // 在layer中加入动画,并约定好该动画的key
   }
})

我们来解释一下上面这段代码究竟做了什么:

options: 选择.lowPriority(oc中是SDWebImageLowPriority),是为了禁止在UI交互过程中启动图像下载,保证列表流畅性。例如,导致UIScrollView减速的下载延迟

判断cacheType == .none: 只有当缓存中没有图片,也就是首次加载时才实现CATransition动画,因为SDWebImage会根据url从缓存中查找是否存在相同的图片,只有当缓存中不存在该图片时才会启动下载,同理,我们的「渐变」效果也是只有当启动下载是才会发生,否则,在列表中复用时会持续显示「渐变」效果,无论是否已经下载完成

然后,我们遵循CAAnimationDelegate协议,并实现animationDidStop(_ anim: CAAnimation, finished flag: Bool)方法:

// MARK: 监听渐变动画是否已完成
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
   if flag {
      self.layer.removeAnimation(forKey: "newVoteTimeline") // 当newVoteTimeline动画已经完成,将其从layer中移除,避免复用中的产生反复「渐变」效果
   }
}

以上就完成了针对SDWebImage框架的「渐变」效果修改,几行代码即可搞定,完全不需要对SDWebImage进行内部修改,有时候换一种思路思考问题,世界会豁然开朗。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,103评论 4 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,116评论 25 707
  •   IDE:Android Studio  无法使用Recyclerview,android.support.v7...
    arousalzk阅读 4,853评论 0 0
  • 后台数据管理 后台采用的Parse.com,真的好好用!而且guide写得超级好,连界面都模仿的苹果的风格,行文清...
    _lemon阅读 450评论 0 0
  • 炎炎夏日,接到七岁侄女打来的电话约我去游泳。侄女住的小区的楼下有个室外的游泳池,很方便。酷暑难耐,我欣然答...
    灵清的玉阅读 659评论 0 5