在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:https://github.com/wty21cn/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/

感谢分享,现在都搞github啦,我就懒得弄。好多网友建议我也发git,嫌只发文章 看不过瘾,那么等到我不忙了也发发git demo

让CALayer的shadowPath跟随bounds一起做动画改变-b的更多相关文章

  1. iOS边练边学--CALayer,非根层隐式动画,钟表练习

    一.CALayer UIView之所以能显示在屏幕上,完全是因为他内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性 ...

  2. 使用requestAnimationFrame做动画效果二

    3月是个好日子,渐渐地开始忙起来了,我做事还是不够细心,加上感冒,没精神,今天差点又出事了,做过的事情还是要检查一遍才行,哎呀. 使用requestAnimationFrame做动画,我做了很久,终于 ...

  3. Android使用XML做动画UI

    在Android应用程序,使用动画效果,能带给用户更好的感觉.做动画可以通过XML或Android代码.本教程中,介绍使用XML来做动画.在这里,介绍基本的动画,如淡入,淡出,旋转等. 效果: htt ...

  4. [UWP]用Shape做动画

    相对于WPF/Silverlight,UWP的动画系统可以说有大幅提高,不过本文无意深入讨论这些动画API,本文将介绍使用Shape做一些进度.等待方面的动画,除此之外也会介绍一些相关技巧. 1. 使 ...

  5. [UWP]用Shape做动画(2):使用与扩展PointAnimation

    上一篇几乎都在说DoubleAnimation的应用,这篇说说PointAnimation. 1. 使用PointAnimation 使用PointAnimation可以让Shape变形,但实际上没看 ...

  6. transition和animation做动画(css动画二)

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! translate:平移:是transform的一个属性: transform:变形:是一个静态属性,可以 ...

  7. WPF中如何调整TabControl的大小,使其跟随Window的大小而改变?

    多年不写技术博客,手生的很,也不知道大家都关注什么,最近在研究Wpf及3d模型的展示,碰到很多问题,这个是最后一个问题,写出来小结一下...... WPF中如何调整TabControl的大小,使其跟随 ...

  8. 沿着path路径做动画

    沿着path路径做动画 路径 效果 源码 // // ViewController.m // PathAnimation // // Created by YouXianMing on 16/1/26 ...

  9. 变换CALayer锚点实现模拟时钟的动画

    变换CALayer锚点实现模拟时钟的动画 变换锚点得需要一点理论知识,看下图就能明白:). https://developer.apple.com/library/ios/documentation/ ...

随机推荐

  1. 使用Gmail发送邮件心得

    /// <summary> /// 发送邮件 /// </summary> /// <returns></returns> public static ...

  2. 关于Css选择器优先级

    今天练习css的时候,重叠后的style发现不起作用,原来css选择器优先级大有文章. 声明: yi下内容选自 51cto.com  --加以自己的理解 以备日后参照使用,毕竟自己理解的才是自己的. ...

  3. 自定义input file 属性

    <label class="input"><input title="浏览文件" type="file" />浏览… ...

  4. windowSoftInputMode属性详解

    转自:http://blog.csdn.net/twoicewoo/article/details/7384398 activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Andro ...

  5. Objective-C设计模式——原型Prototype(对象创建)

    1.原型 原型设计模式所谓原型设计模式,其实就是对象复制,这个特性在所有语言基本上都是存在的. 我们知道在OC中,对象赋值其实是对对象的引用复制,其实就是相当于C语言中的指针.创建了一个新的变量,但是 ...

  6. linux工程管理工具make入门

    一.make工具的功能 1.主要负责一个软件工程中多个源代码的自动编译工作 2.还能进行环境检测.后期处理等工作: 3.make工具可以识别出工程中哪些文件已经被修改,并且在再次编译的时候只编译这些文 ...

  7. jquery.cookie.js 使用方法

    Cookies 定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术: 下载与引入:jquery.cookie.js基于jquery:先引入jquery,再引入:jq ...

  8. 一个线程间的通讯小程序__(Java_Thread_Inout.Output)

    //多线程通讯 //多个线程处理同一资源,但是任务不同 //等待唤醒方法: //wait():将线程变成为冻结状态,线程会被存储在线程池中; //notify():唤醒线程中的一个线程(任意的) // ...

  9. gcc与g++的区别

    一:gcc与g++比较 编译c/c++代码的时候,有人用gcc,有人用g++,于是各种说法都来了,譬如c代码用gcc,而 c++代码用g++,或者说编译用gcc,链接用g++,一时也不知哪个说法正确, ...

  10. <Linux下FTP服务的搭建>

    默认安装好ftp软件包匿名用户是可以下载的.匿名以后可以上传:anon_upload_enable=YES# getsebool -a | grep ftpallow_ftpd_anon_write ...