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

在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

时间: 2024-10-25 16:49:42

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

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

一.CALayer UIView之所以能显示在屏幕上,完全是因为他内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层 当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘制,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示 UIView本身不具备显示的功能,是他内部的层才有显示功能 二.CALayer的基本使用 三.关于CALa

沿着贝塞尔曲线做动画

效果图如下: 源码: // // RootViewController.m // // Copyright (c) 2014年 Y.X. All rights reserved. // #import "RootViewController.h" #import "YXGCD.h" @interface RootViewController () @property (nonatomic, strong) GCDTimer *timer; @end @impleme

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

前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! translate:平移:是transform的一个属性: transform:变形:是一个静态属性,可以改变元素的形状或位置,做出2d或3d效果: transition:过渡,转变:使css属性值在一段时间内平滑的变化,需要有触发条件(如hover等),是animation的简化版: animation:动画:可以设置多帧效果,然后把它们组合变换,按动画效果展示出来: 1. transition 过渡

用杯赛尔曲线(做动画和绘图)

1. 用被塞尔曲线做动画 效果:位置沿着贝瑟尔曲线位置移动,尺寸由大到小,透明度从完全可见过渡到彻底透明. 至于在DrawRect里面绘制贝塞尔曲线,可以直接百度,没有什么难点的. - (void)clickButton:(id)sender { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:btn.center]; [path addCurveToPoint:btn.center controlPoint1:

使用requestAnimationFrame做动画效果二

3月是个好日子,渐渐地开始忙起来了,我做事还是不够细心,加上感冒,没精神,今天差点又出事了,做过的事情还是要检查一遍才行,哎呀. 使用requestAnimationFrame做动画,我做了很久,终于在二月底完工了,中间绕了不少弯路,先把domo放出来: 要实行的功能显而易见,一开始我粗糙地不考虑性能,不使用requestAnimationFrame,已经做出来了,但是觉得不好,于是进行了进一步改进.采用对象的方法,先生成10个粉色的小方块,隐藏起来,然后利用requestAnimationFr

做动画的一大接口 requestAnimationFrame

要实现动画效果,可以有以下几种实现: 1.setInterval setTimeout 2.css3 transition 3.requestAnimationFrame requestAnimationFrame是浏览器自带的api,传入参数为动画效果的执行函数. 因为是浏览器自带的函数,所以执行动画的频率和浏览器显示频率是一致的(目前大部分浏览器显示频率为16.7ms,即1000/60ms),并且在切换到别的tab页之后,也就是页面没被激活时是不执行动画的.和setInterval setT

锐浪 报表, 交叉报表中 对交叉字段,做条件改变背景颜色 .

var fieldCount = Report.RunningDetailGrid.ColumnContent.ContentCells.Count; // 总字段列数 var lockFieldCount = Report.DetailGrid.CrossTab.ListCols; // 锁定字段列数 var crossFieldCount = fieldCount - lockFieldCount; for(var colIndex = 1; colIndex <=crossFieldCou

解决在圆形内做动画而不超出圆外的解决办法

注意:此次动画的目的是在圆内做动画,而动画效果不会超出圆的范围外 在制作这个效果的时候,我一开始想到的就是以下的做法: CSS: .circle{width: 200px;height: 200px;border-radius: 200px;background: rgba(0,0,0,0.5);overflow: hidden;position: absolute;} .text{position: absolute;top: 0;left: 0;-webkit-transform:trans

TCG卡牌游戏研究:《炉石战记:魔兽英雄传》所做的改变

转自:http://www.gameres.com/665306.html TCG演进史 说到卡牌游戏,大家会联想到什么呢? 是历史悠久的扑克牌.风靡全球的<MTG 魔法风云会>与<游戏王>.结合数位与现实的<三国志大战>.或是在手机上掀起收集热潮的<龙族拼图>和<百万亚瑟王>? 卡牌游戏这个统称,其内容可以跟各式各样的玩法结合,而暴风雪新推出的<炉石战记>(以下简称炉石)所选择的玩法,是让玩家自行组牌.进行对战的「集换式卡牌游戏」(