让CALayer的shadowPath跟随bounds一起做动画改变

前言

在iOS开发中,我们经常需要给视图添加阴影效果,最简单的方法就是通过设置CALayer的shadowColor、shadowOpacity、shadowOffset和shadowRadius这几个属性可以很方便的为 UIView 添加阴影效果。但是如果单用这几个属性会导致离屏渲染(Offscreen Rendering),而且CoreAnimation在每一帧绘制阴影的时候都需要递归遍历所有sublayer的alpha通道从而精确的计算出阴影的轮廓,这是非常消耗性能的,从而导致了动画的卡顿。

为了尽可能地减小离屏渲染带来的性能影响,我们可以利用CALayer的另外一个属性shadowPath,这个属性的官方文档是这么描述的:

If you specify a value for this property, the layer creates its shadow using the specified path instead of the layer’s composited alpha channel. The path you provide defines the outline of the shadow. It is filled using the non-zero winding rule and the current shadow color, opacity, and blur radius.

可以看到设置了这个属性以后CALayer在创建其shadow的时候不在遍历sublayer的alpha通道,而是直接用这个属性所指定的路径作为阴影的轮廓,这样就减少了非常多的计算量。

然而这里会有一个问题,shadowPath并不会跟随CALayer的bounds属性进行变化,所以在layer的bounds产生变化以后需要手动更新shadowPath才能让其适配新的bounds。

为了解决这个问题,在使用AutoLayout以前,因为bounds都是手动计算出来的,我们可以很容易的直接设定新的shadowPath,而使用了AutoLayout以后,我们则只能在UIView的layoutSubivews方法中才能获得更新后的bounds。

而且文档中还做了如下描述:

Unlike most animatable properties, this property (as with all CGPathRef animatable properties) does not support implicit animation.

这说明该变量是不支持隐式动画的,也就是说当我们直接设置CALayer的shadowPath属性后,系统并不会自动的提交隐式的CATransaction动画。

为了解决了这个问题,我们需要通过CABasicAnimation显示地指定shadowPath的动画效果,同时为了和bounds的动画效果保持一致,我们需要获取bounds的动画属性。

考虑了以上两点问题以后,我们就可以用如下方法实现让CALayer的shadowPath跟随bounds一起做动画改变。

要特别注意一点的是,在iOS8以后bounds的隐式动画默认是开启additive模式的,而CALayer的shadowPath属性并不支持additive模式,所以如果在前一个shadowPath动画执行完毕前如果提交了新的动画,使用本方法将会看到shadowPath和bounds的动画不一致的现象。在Demo中可快速点击改变Bounds的按钮来复现该现象。

实现方法

为实现本文的思路,我们需要创建一个一个UIView的子类并且重写其layoutSubivew方法。

// Subclass of UIView
- (void)layoutSubviews {
    [super layoutSubviews];
    if (self.shouldAnimateShadowPath) {
        CAAnimation *animation = [self.layer animationForKey:@"bounds.size"];
        if (animation) {
            // 通过CABasicAnimation类来为shadowPath添加动画
            CABasicAnimation *shadowPathAnimation = [CABasicAnimation animationWithKeyPath:@"shadowPath"];
            // 根据bounds的动画属性设置shadowPath的动画属性
            shadowPathAnimation.timingFunction = animation.timingFunction;
            shadowPathAnimation.duration = animation.duration;
            // iOS8 bounds的隐式动画默认开启了additive属性,当前一次bounds change的动画还在进行中时,
            // 新的bounds change动画将会被叠加在之前的上,从而让动画更加顺滑
            // 然而shadowPath并不支持additive animation,所以当多个动画叠加,将会看到shadowPath和bounds动画不一致的现象
            // shadowPathAnimation.additive = YES;
            
            // 设置shadowAnimation的新值,未设置from,则from属性将默认为当前shadowPath的状态
            shadowPathAnimation.toValue = [UIBezierPath bezierPathWithRect:self.layer.bounds];
            
            // 将动画添加至layer的渲染树
            [self.layer addAnimation:shadowPathAnimation forKey:@"shadowPath"];
        }
        // 根据苹果文档指出的,显式动画只会影响动画效果,而不会影响属性的的值,所以这两为了持久化shadowPath的改变需要进行属性跟新
        // 同时也处理了bounds非动画改变的情况
        self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
    }
}

Demo

本文Demo地址为ShadowPathAnimationDemo

disableShadowPathAnimation.gif

可以看到当关闭shadowPath动画,也就是不执行上述代码的时候,当view的bounds改变以后shadowPath还为原来的值,并未跟随bounds进行改变。

enableShadowPathAnimation.gif

可以看到当打开shadowPath动画,也就是要执行了上述代码时,当view的bounds改变以后shadowPath能够跟随一起改变,而且动画效果相同。


本文个人博客地址: http://wty.im/2016/09/26/let-shadow-path-animate-with-layer-bounds/
Github: https://github.com/wty21cn/

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,299评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 4,975评论 5 13
  • Core Animation其实是一个令人误解的命名。你可能认为它只是用来做动画的,但实际上它是从一个叫做Laye...
    小猫仔阅读 3,511评论 1 4
  • 转载:http://www.cnblogs.com/jingdizhiwa/p/5601240.html 1.ge...
    F麦子阅读 1,434评论 0 1
  • 转载:http://www.jianshu.com/p/32fcadd12108 每个UIView有一个伙伴称为l...
    F麦子阅读 5,898评论 0 13