iOS动画篇:核心动画

KatJudkins 9年前
   <h3>基本概念</h3>    <p>1、什么是核心动画</p>    <p>        Core Animation(核心动画)是一组功能强大、效果华丽的动画API,无论在iOS系统或者在你开发的App中,都有大量应用。<br>         核心动画所在的位置如下图所示:</p>    <p><img src="https://simg.open-open.com/show/b593918ccd20434a2e4ec67fd14c1f54.png" alt="iOS动画篇:核心动画" width="300" height="222"></p>    <p>Core Animation.png</p>    <p><br>         可以看到,核心动画位于UIKit的下一层,相比UIView动画,它可以实现更复杂的动画效果。</p>    <p>        核心动画作用在CALayer(Core animation layer)上,CALayer从概念上类似UIView,我们可以将UIView看成是一种特殊的CALayer(可以响应事件)。<br>         实际上,每一个view都有其对应的layer,这个layer是root layer:</p>    <pre>  <code class="language-objectivec">    @property(nonatomic,readonly,strong)                 CALayer  *layer;</code></pre>    <p>        给view加上动画,本质上是对其layer进行操作,layer包含了各种支持动画的属性,动画则包含了属性变化的值、变化的速度、变化的时间等等,两者结合产生动画的过程。</p>    <p>        核心动画和UIView动画的对比:UIView动画可以看成是对核心动画的封装,和UIView动画不同的是,通过核心动画改变layer的状态(比如position),动画执行完毕后实际上是没有改变的(表面上看起来已改变)。</p>    <p>        总体来说核心动画的优点有:</p>    <p>        1)性能强大,使用硬件加速,可以同时向多个图层添加不同的动画效果<br>         2)接口易用,只需要少量的代码就可以实现复杂的动画效果。<br>         3)运行在后台线程中,在动画过程中可以响应交互事件(UIView动画默认动画过程中不响应交互事件)。</p>    <p>2、核心动画类的层次结构</p>    <p><img src="https://simg.open-open.com/show/054fe81fa7828e37658a8ced8bf639dd.png" alt="iOS动画篇:核心动画" width="428" height="458"></p>    <p>Core Animation classes and protocol.png</p>    <p><br>         CAAnimation是所有动画对象的父类,实现CAMediaTiming协议,负责控制动画的时间、速度和时间曲线等等,是一个抽象类,不能直接使用。<br>         CAPropertyAnimation :是CAAnimation的子类,它支持动画地显示图层的keyPath,一般不直接使用。<br>         iOS9.0之后新增CASpringAnimation类,它实现弹簧效果的动画,是CABasicAnimation的子类。</p>    <p>        综上,核心动画类中可以直接使用的类有:</p>    <p>        CABasicAnimation<br>         CAKeyframeAnimation<br>         CATransition<br>         CAAnimationGroup<br>         CASpringAnimation</p>    <p>3、核心动画类的核心方法</p>    <p>        1.初始化CAAnimation对象<br>         一般使用animation方法生成实例</p>    <pre>  <code class="language-objectivec">     + (instancetype)animation;</code></pre>    <p>        如果是CAPropertyAnimation的子类,还可以通过animationWithKeyPath生成实例</p>    <pre>  <code class="language-objectivec">     + (instancetype)animationWithKeyPath:(nullable NSString *)path;</code></pre>    <p>        2.设置动画的相关属性<br>         设置动画的执行时间,执行曲线,keyPath的目标值,代理等等</p>    <p>        3.动画的添加和移除<br>         调用CALayer的addAnimation:forKey:方法将动画添加到CALayer中,这样动画就开始执行了</p>    <pre>  <code class="language-objectivec">     - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;</code></pre>    <p>        调用CALayer的removeAnimation方法停止CALayer中的动画</p>    <pre>  <code class="language-objectivec">     - (void)removeAnimationForKey:(NSString *)key;       - (void)removeAllAnimations;</code></pre>    <p>4、核心动画类的常用属性</p>    <p>        keyPath:可以指定keyPath为CALayer的属性值,并对它的值进行修改,以达到对应的动画效果,需要注意的是部分属性值是不支持动画效果的。<br>         以下是具有动画效果的keyPath:</p>    <pre>  <code class="language-objectivec">     //CATransform3D Key Paths : (example)transform.rotation.z       //rotation.x       //rotation.y       //rotation.z       //rotation 旋轉       //scale.x       //scale.y       //scale.z       //scale 缩放       //translation.x       //translation.y       //translation.z       //translation 平移         //CGPoint Key Paths : (example)position.x       //x       //y         //CGRect Key Paths : (example)bounds.size.width       //origin.x       //origin.y       //origin       //size.width       //size.height       //size         //opacity       //backgroundColor       //cornerRadius        //borderWidth       //contents          //Shadow Key Path:       //shadowColor        //shadowOffset        //shadowOpacity        //shadowRadius</code></pre>    <p>        duration:动画的持续时间<br>         repeatCount:动画的重复次数<br>         timingFunction:动画的时间节奏控制</p>    <pre>  <code class="language-objectivec">     timingFunctionName的enum值如下:       kCAMediaTimingFunctionLinear 匀速       kCAMediaTimingFunctionEaseIn 慢进       kCAMediaTimingFunctionEaseOut 慢出       kCAMediaTimingFunctionEaseInEaseOut 慢进慢出       kCAMediaTimingFunctionDefault 默认值(慢进慢出)</code></pre>    <p>        fillMode:视图在非Active时的行为<br>         removedOnCompletion:动画执行完毕后是否从图层上移除,默认为YES(视图会恢复到动画前的状态),可设置为NO(图层保持动画执行后的状态,前提是fillMode设置为kCAFillModeForwards)<br>         beginTime:动画延迟执行时间(通过CACurrentMediaTime() + your time 设置)<br>         delegate:代理</p>    <pre>  <code class="language-objectivec">    代理方法如下:       - (void)animationDidStart:(CAAnimation *)anim;  //动画开始       - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; //动画结束</code></pre>    <h3>CABasicAnimation</h3>    <p>        CABasicAnimation可以设定keyPath的起点,终点的值,动画会沿着设定点进行移动,CABasicAnimation可以看成是只有两个关键点的特殊的CAKeyFrameAnimation。</p>    <p>下面以改变视图的position为例演示其使用:</p>    <pre>  <code class="language-objectivec">- (void)position {      CABasicAnimation * ani = [CABasicAnimation animationWithKeyPath:@"position"];      ani.toValue = [NSValue valueWithCGPoint:self.centerShow.center];      ani.removedOnCompletion = NO;      ani.fillMode = kCAFillModeForwards;      ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];      [self.cartCenter.layer addAnimation:ani forKey:@"PostionAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/b34d4bde517502f048bb2f0d4e0430e1.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>position.gif</p>    <p>        下面是部分keyPath对应的动画效果(图片名为其对应的keyPath):</p>    <p><img src="https://simg.open-open.com/show/abd2fbd0a42749818f354112ab79ec5d.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>positionX.gif</p>    <p><img src="https://simg.open-open.com/show/a74153771ab8752011c0edd421bef632.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>positionY.gif</p>    <p><img src="https://simg.open-open.com/show/4d9e5556efad4163c7ae489ee54b9f98.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>rotation.gif</p>    <p><img src="https://simg.open-open.com/show/8d270959c06871d581250221968878ea.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>rotationX.gif</p>    <p><img src="https://simg.open-open.com/show/fbd41aa58a7102ca201754fa66ef262d.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>rotationY.gif</p>    <p><img src="https://simg.open-open.com/show/aa2e0e8a34df533ecfb8793458a9aabf.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>rotationZ.gif</p>    <p><img src="https://simg.open-open.com/show/ca4d2f0f26668b5604a1fa97ac95a2a4.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>scale.gif</p>    <p><img src="https://simg.open-open.com/show/fc5e01adf6cc2bb47f3df5fe1485cfdb.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>translation.gif</p>    <p><img src="https://simg.open-open.com/show/7d2b37914d2f2258fb6207a305ab1e3e.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>transform的组合效果.gif</p>    <p><img src="https://simg.open-open.com/show/1bdb3fa873758c53bff3fbcbccb7bac2.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>bounds.gif</p>    <p><img src="https://simg.open-open.com/show/ffcb4b1de2af6859ce89b9236868b999.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>size.gif</p>    <p><img src="https://simg.open-open.com/show/831753b02f6fef4e3e37e9325394a25c.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>sizeW.gif</p>    <p><img src="https://simg.open-open.com/show/a82bfccc8566d3322f34460e6a182740.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>sizeH.gif</p>    <p><img src="https://simg.open-open.com/show/100734012ea5c4fed1b17562badce693.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>opacity.gif</p>    <p><img src="https://simg.open-open.com/show/c04dca2afd9f8cf71139e71cfaab8594.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>backgroundColor.gif</p>    <p><img src="https://simg.open-open.com/show/dd2807d666ff5b2d9dac939a05765d35.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>cornerRadius.gif</p>    <p><img src="https://simg.open-open.com/show/02edead723873cadec841e183fc5aacf.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>borderWidth.gif</p>    <p><img src="https://simg.open-open.com/show/126b6835fd6d766cf9b0096ebc980177.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>contents.gif</p>    <p><img src="https://simg.open-open.com/show/200a8934c4197d50647f30cd3d898828.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>shadowColor.gif</p>    <p><img src="https://simg.open-open.com/show/ed52a3caa36225570a03d1bb3a4f1e41.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>shadowOffset.gif</p>    <p><img src="https://simg.open-open.com/show/5a9d9e4c6260d5ded8beaa42a5dd0043.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>shadowOpacity.gif</p>    <p><img src="https://simg.open-open.com/show/eeade59f65b9b2b6a667cc337a07086b.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>shadowRadius.gif</p>    <h3>CAKeyframeAnimation</h3>    <p>        可以设定keyPath起点、中间关键点(不止一个)、终点的值,每一帧所对应的时间,动画会沿着设定点进行移动。</p>    <p>        CAKeyframeAnimation的重要属性:</p>    <p>        values:关键帧数组对象,里面每一个元素即为一个关键帧,动画会在对应的时间段内,依次执行数组中每一个关键帧的动画。<br>         path:动画路径对象,可以指定一个路径,在执行动画时路径会沿着路径移动,Path在动画中只会影响视图的Position。<br>         keyTimes:设置关键帧对应的时间点,范围:0-1。如果没有设置该属性,则每一帧的时间平分。</p>    <p>        下面以让视图绕圈为例演示其使用:</p>    <p>        1、设置values使其沿正方形运动</p>    <pre>  <code class="language-objectivec">- (void)valueKeyframeAni {      CAKeyframeAnimation * ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];      ani.duration = 4.0;      ani.removedOnCompletion = NO;      ani.fillMode = kCAFillModeForwards;      ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];      NSValue * value1 = [NSValue valueWithCGPoint:CGPointMake(150, 200)];      NSValue *value2=[NSValue valueWithCGPoint:CGPointMake(250, 200)];      NSValue *value3=[NSValue valueWithCGPoint:CGPointMake(250, 300)];      NSValue *value4=[NSValue valueWithCGPoint:CGPointMake(150, 300)];      NSValue *value5=[NSValue valueWithCGPoint:CGPointMake(150, 200)];      ani.values = @[value1, value2, value3, value4, value5];      [self.centerShow.layer addAnimation:ani forKey:@"PostionKeyframeValueAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/fb22e4c01beeaf71be4091a1074e528c.gif" alt="iOS动画篇:核心动画" width="372" height="327"></p>    <p>valueKeyframeAni.gif</p>    <p>        2、设置path使其绕圆圈运动</p>    <pre>  <code class="language-objectivec">- (void)pathKeyframeAni {      CAKeyframeAnimation * ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];      CGMutablePathRef path = CGPathCreateMutable();      CGPathAddEllipseInRect(path, NULL, CGRectMake(130, 200, 100, 100));      ani.path = path;      CGPathRelease(path);      ani.duration = 4.0;      ani.removedOnCompletion = NO;      ani.fillMode = kCAFillModeForwards;      [self.centerShow.layer addAnimation:ani forKey:@"PostionKeyframePathAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/043437374f6bacf8871728021c9b3a40.gif" alt="iOS动画篇:核心动画" width="372" height="327"></p>    <p>pathKeyframeAni.gif</p>    <h3>CATransition</h3>    <p>        转场动画,比UIVIew转场动画具有更多的动画效果,比如Nav的默认Push视图的效果就是通过CATransition的kCATransitionPush类型来实现。</p>    <p>        CAKeyframeAnimation的重要属性:</p>    <p>        type:过渡动画的类型</p>    <pre>  <code class="language-objectivec">    type的enum值如下:      kCATransitionFade 渐变      kCATransitionMoveIn 覆盖      kCATransitionPush 推出      kCATransitionReveal 揭开</code></pre>    <p>        还有一些私有动画类型,效果很炫酷,不过不推荐使用。<br>         私有动画类型的值有:"cube"、"suckEffect"、"oglFlip"、 "rippleEffect"、"pageCurl"、"pageUnCurl"等等。</p>    <p>        subtype: 过渡动画的方向</p>    <pre>  <code class="language-objectivec">    subtype的enum值如下:      kCATransitionFromRight 从右边      kCATransitionFromLeft 从左边      kCATransitionFromTop 从顶部      kCATransitionFromBottom 从底部</code></pre>    <p>        以渐变效果为例</p>    <pre>  <code class="language-objectivec">- (void)transitionAni {      CATransition * ani = [CATransition animation];      ani.type = kCATransitionFade;      ani.subtype = kCATransitionFromLeft;      ani.duration = 1.5;      self.centerShow.image = [UIImage imageNamed:@"Raffle"];      [self.centerShow.layer addAnimation:ani forKey:@"transitionAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/5f755b6ae63142dce2674b11e9cc47a4.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>kCATransitionFade.gif</p>    <p>以下是另外三种转场类型的动画效果(图片名称对应其type类型):</p>    <p><img src="https://simg.open-open.com/show/6d5dc5040660548dfc6271daba5c4bb1.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>kCATransitionMoveIn.gif</p>    <p><img src="https://simg.open-open.com/show/e71da4e8d872eada7e8f366a7a8079c9.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>kCATransitionPush.gif</p>    <p><img src="https://simg.open-open.com/show/1616afea513e03ccf1056cc5dddcc4b9.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>kCATransitionReveal.gif</p>    <p>以下是部分私有转场类型的动画效果(图片名称对应其type类型):</p>    <p><img src="https://simg.open-open.com/show/80c51e8804232c4a1587ad8bbb58686f.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>transitionAni - rippleEffect.gif</p>    <p><img src="https://simg.open-open.com/show/ddea380ab2c992c5a134116c4d04fb74.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>transitionAni - cube.gif</p>    <p><img src="https://simg.open-open.com/show/51fbcb942e5fe54110ca67bdd43fd53d.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>transitionAni - pageCurl.gif</p>    <p><img src="https://simg.open-open.com/show/a919080f9d7a6c90b6fcf55368d8a6e4.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>transitionAni - suckEffect.gif</p>    <h3>CASpringAnimation</h3>    <p>        CASpringAnimation是iOS9新加入动画类型,是CABasicAnimation的子类,用于实现弹簧动画。</p>    <p>        CASpringAnimation的重要属性:</p>    <p>        mass:质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)<br>         stiffness:弹性系数(弹性系数越大,弹簧的运动越快)<br>         damping:阻尼系数(阻尼系数越大,弹簧的停止越快)<br>         initialVelocity:初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)<br>         settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)</p>    <p>        CASpringAnimation和UIView的SpringAnimation对比:</p>    <p>        1.CASpringAnimation 可以设置更多影响弹簧动画效果的属性,可以实现更复杂的弹簧动画效果,且可以和其他动画组合。<br>         2.UIView的SpringAnimation实际上就是通过CASpringAnimation来实现。</p>    <p>        以实现视图的bounds变化的弹簧动画效果为例:</p>    <pre>  <code class="language-objectivec">- (void)springAni {      CASpringAnimation * ani = [CASpringAnimation animationWithKeyPath:@"bounds"];      ani.mass = 10.0; //质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大      ani.stiffness = 5000; //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快      ani.damping = 100.0;//阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快      ani.initialVelocity = 5.f;//初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反      ani.duration = ani.settlingDuration;      ani.toValue = [NSValue valueWithCGRect:self.centerShow.bounds];      ani.removedOnCompletion = NO;      ani.fillMode = kCAFillModeForwards;      ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];      [self.cartCenter.layer addAnimation:ani forKey:@"boundsAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/342e58924429de861df889218320428d.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>springAni.gif</p>    <h3>CAAnimationGroup</h3>    <p>        使用Group可以将多个动画合并一起加入到层中,Group中所有动画并发执行,可以方便地实现需要多种类型动画的场景。</p>    <p>        以实现视图的position、bounds和opacity改变的组合动画为例</p>    <pre>  <code class="language-objectivec">- (void)groupAni {      CABasicAnimation * posAni = [CABasicAnimation animationWithKeyPath:@"position"];      posAni.toValue = [NSValue valueWithCGPoint:self.centerShow.center];        CABasicAnimation * opcAni = [CABasicAnimation animationWithKeyPath:@"opacity"];      opcAni.toValue = [NSNumber numberWithFloat:1.0];      opcAni.toValue = [NSNumber numberWithFloat:0.7];        CABasicAnimation * bodAni = [CABasicAnimation animationWithKeyPath:@"bounds"];      bodAni.toValue = [NSValue valueWithCGRect:self.centerShow.bounds];        CAAnimationGroup * groupAni = [CAAnimationGroup animation];      groupAni.animations = @[posAni, opcAni, bodAni];      groupAni.duration = 1.0;      groupAni.fillMode = kCAFillModeForwards;      groupAni.removedOnCompletion = NO;      groupAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];      [self.cartCenter.layer addAnimation:groupAni forKey:@"groupAni"];  }</code></pre>    <p>        动画效果:</p>    <p><img src="https://simg.open-open.com/show/6b5b1c53f26619ca692953879b57d7f1.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p>    <p>groupAni.gif</p>    <h3>CATransaction</h3>    <p>        最后讲一下事务,在核心动画里面存在事务(CATransaction)这样一个概念,它负责协调多个动画原子更新显示操作。<br>         简单来说事务是核心动画里面的一个基本的单元,动画的产生必然伴随着layer的Animatable属性的变化,而layer属性的变化必须属于某一个事务。<br>         因此,核心动画依赖事务。</p>    <p>        事务的作用:保证一个或多个layer的一个或多个属性变化同时进行<br>         事务分为隐式和显式:<br>         1.隐式:没有明显调用事务的方法,由系统自动生成事务。比如直接设置一个layer的position属性,则会在当前线程自动生成一个事务,并在下一个runLoop中自动commit事务。<br>         2.显式:明显调用事务的方法([CATransaction begin]和[CATransaction commit])。</p>    <p>        CA事务的可设置属性(会覆盖隐式动画的设置):</p>    <pre>  <code class="language-objectivec">    animationDuration:动画时间      animationTimingFunction:动画时间曲线      disableActions:是否关闭动画      completionBlock:动画执行完毕的回调</code></pre>    <p>        事务支持嵌套使用:当最外层的事务commit后动画才会开始。</p>    <p>        使用实例:</p>    <pre>  <code class="language-objectivec">    [CATransaction begin];      [CATransaction setAnimationDuration:2.0];      [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];  //    [CATransaction setDisableActions:YES]; //设置为YES就关闭动画      self.subLayer.bounds = self.centerShow.layer.bounds;      [CATransaction commit];</code></pre>    <p>        注意:只有非root layer才有隐式动画,如果你是直接设置</p>    <p><code>self.cartCenter.layer.bounds = self.centerShow.layer.bounds;</code>是不会有动画效果的。</p>    <p>Next</p>    <p>        接下来将更新iOS绘图引擎Quartz2D</p>    <p><a href="/misc/goto?guid=4959671776929246665">文/明仔Su(简书作者)</a><br>  </p>