iOS动画篇:核心动画
etqt2836
9年前
<h2>基本概念</h2> <h3>1、什么是核心动画</h3> <p>Core Animation(核心动画)是一组功能强大、效果华丽的动画API,无论在iOS系统或者在你开发的App中,都有大量应用。</p> <p>核心动画所在的位置如下图所示:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b593918ccd20434a2e4ec67fd14c1f54.png"></p> <p>可以看到,核心动画位于UIKit的下一层,相比UIView动画,它可以实现更复杂的动画效果。</p> <p>核心动画作用在CALayer(Core animation layer)上,CALayer从概念上类似UIView,我们可以将UIView看成是一种特殊的CALayer(可以响应事件)。</p> <p>实际上,每一个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)性能强大,使用硬件加速,可以同时向多个图层添加不同的动画效果</p> <p>2)接口易用,只需要少量的代码就可以实现复杂的动画效果。</p> <p>3)运行在后台线程中,在动画过程中可以响应交互事件(UIView动画默认动画过程中不响应交互事件)。</p> <h3>2、核心动画类的层次结构</h3> <p style="text-align:center"><img src="https://simg.open-open.com/show/054fe81fa7828e37658a8ced8bf639dd.png"></p> <p>CAAnimation是所有动画对象的父类,实现CAMediaTiming协议,负责控制动画的时间、速度和时间曲线等等,是一个抽象类,不能直接使用。</p> <p>CAPropertyAnimation :是CAAnimation的子类,它支持动画地显示图层的keyPath,一般不直接使用。</p> <p>iOS9.0之后新增CASpringAnimation类,它实现弹簧效果的动画,是CABasicAnimation的子类。</p> <p>综上,核心动画类中可以直接使用的类有:</p> <p>CABasicAnimation</p> <p>CAKeyframeAnimation</p> <p>CATransition</p> <p>CAAnimationGroup</p> <p>CASpringAnimation</p> <h3>3、核心动画类的核心方法</h3> <p>1.初始化CAAnimation对象</p> <p>一般使用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.设置动画的相关属性</p> <p>设置动画的执行时间,执行曲线,keyPath的目标值,代理等等</p> <p>3.动画的添加和移除</p> <p>调用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的属性值,并对它的值进行修改,以达到对应的动画效果,需要注意的是部分属性值是不支持动画效果的。</p> <p>以下是具有动画效果的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:动画的持续时间</p> <p>repeatCount:动画的重复次数</p> <p>timingFunction:动画的时间节奏控制</p> <pre> <code class="language-objectivec"> timingFunctionName的enum值如下: kCAMediaTimingFunctionLinear 匀速 kCAMediaTimingFunctionEaseIn 慢进 kCAMediaTimingFunctionEaseOut 慢出 kCAMediaTimingFunctionEaseInEaseOut 慢进慢出 kCAMediaTimingFunctionDefault 默认值(慢进慢出)</code></pre> <p>fillMode:视图在非Active时的行为</p> <p>removedOnCompletion:动画执行完毕后是否从图层上移除,默认为YES(视图会恢复到动画前的状态),可设置为NO(图层保持动画执行后的状态,前提是fillMode设置为kCAFillModeForwards)</p> <p>beginTime:动画延迟执行时间(通过CACurrentMediaTime() + your time 设置)</p> <p>delegate:代理</p> <pre> <code class="language-objectivec">代理方法如下: - (void)animationDidStart:(CAAnimation *)anim; //动画开始 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; //动画结束</code></pre> <p>CABasicAnimation</p> <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 style="text-align:center"><img src="https://simg.open-open.com/show/b34d4bde517502f048bb2f0d4e0430e1.gif"></p> <p>下面是部分keyPath对应的动画效果(图片名为其对应的keyPath):</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b34d4bde517502f048bb2f0d4e0430e1.gif" alt="iOS动画篇:核心动画" width="372" height="292"></p> <p style="text-align:center">positionX.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a74153771ab8752011c0edd421bef632.gif"></p> <p style="text-align:center">positionY.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4d9e5556efad4163c7ae489ee54b9f98.gif"></p> <p style="text-align:center">rotation.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8d270959c06871d581250221968878ea.gif"></p> <p style="text-align:center">rotationX.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fbd41aa58a7102ca201754fa66ef262d.gif"></p> <p style="text-align:center">rotationY.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/aa2e0e8a34df533ecfb8793458a9aabf.gif"></p> <p style="text-align:center">rotationZ.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ca4d2f0f26668b5604a1fa97ac95a2a4.gif"></p> <p style="text-align:center">scale.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fc5e01adf6cc2bb47f3df5fe1485cfdb.gif"></p> <p style="text-align:center">translation.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/7d2b37914d2f2258fb6207a305ab1e3e.gif"></p> <p style="text-align:center">transform的组合效果.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1bdb3fa873758c53bff3fbcbccb7bac2.gif"></p> <p style="text-align:center">bounds.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ffcb4b1de2af6859ce89b9236868b999.gif"></p> <p style="text-align:center">size.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/831753b02f6fef4e3e37e9325394a25c.gif"></p> <p style="text-align:center">sizeW.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a82bfccc8566d3322f34460e6a182740.gif"></p> <p style="text-align:center">sizeH.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/100734012ea5c4fed1b17562badce693.gif"></p> <p style="text-align:center">opacity.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c04dca2afd9f8cf71139e71cfaab8594.gif"></p> <p style="text-align:center">backgroundColor.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/dd2807d666ff5b2d9dac939a05765d35.gif"></p> <p style="text-align:center">cornerRadius.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/02edead723873cadec841e183fc5aacf.gif"></p> <p style="text-align:center">borderWidth.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/126b6835fd6d766cf9b0096ebc980177.gif"></p> <p style="text-align:center">contents.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/200a8934c4197d50647f30cd3d898828.gif"></p> <p style="text-align:center">shadowColor.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ed52a3caa36225570a03d1bb3a4f1e41.gif"></p> <p style="text-align:center">shadowOffset.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5a9d9e4c6260d5ded8beaa42a5dd0043.gif"></p> <p style="text-align:center">shadowOpacity.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/eeade59f65b9b2b6a667cc337a07086b.gif"></p> <p style="text-align:center">shadowRadius.gif</p> <h2>CAKeyframeAnimation</h2> <p>可以设定keyPath起点、中间关键点(不止一个)、终点的值,每一帧所对应的时间,动画会沿着设定点进行移动。</p> <p>CAKeyframeAnimation的重要属性:</p> <p>values:关键帧数组对象,里面每一个元素即为一个关键帧,动画会在对应的时间段内,依次执行数组中每一个关键帧的动画。</p> <p>path:动画路径对象,可以指定一个路径,在执行动画时路径会沿着路径移动,Path在动画中只会影响视图的Position。</p> <p>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 style="text-align:center"><img src="https://simg.open-open.com/show/fb22e4c01beeaf71be4091a1074e528c.gif"></p> <p style="text-align:center">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 style="text-align:center"><img src="https://simg.open-open.com/show/043437374f6bacf8871728021c9b3a40.gif"></p> <p style="text-align:center">pathKeyframeAni.gif</p> <h2>CATransition</h2> <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>还有一些私有动画类型,效果很炫酷,不过不推荐使用。</p> <p>私有动画类型的值有:"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 style="text-align:center"><img src="https://simg.open-open.com/show/5f755b6ae63142dce2674b11e9cc47a4.gif"></p> <p style="text-align:center">kCATransitionFade.gif</p> <p>以下是另外三种转场类型的动画效果(图片名称对应其type类型):</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6d5dc5040660548dfc6271daba5c4bb1.gif"></p> <p style="text-align:center">kCATransitionMoveIn.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/e71da4e8d872eada7e8f366a7a8079c9.gif"></p> <p style="text-align:center">kCATransitionPush.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1616afea513e03ccf1056cc5dddcc4b9.gif"></p> <p style="text-align:center">kCATransitionReveal.gif</p> <p>以下是部分私有转场类型的动画效果(图片名称对应其type类型):</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/80c51e8804232c4a1587ad8bbb58686f.gif"></p> <p style="text-align:center">transitionAni - rippleEffect.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ddea380ab2c992c5a134116c4d04fb74.gif"></p> <p style="text-align:center">transitionAni - cube.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/51fbcb942e5fe54110ca67bdd43fd53d.gif"></p> <p style="text-align:center">transitionAni - pageCurl.gif</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a919080f9d7a6c90b6fcf55368d8a6e4.gif"></p> <p style="text-align:center">transitionAni - suckEffect.gif</p> <h2>CASpringAnimation</h2> <p>CASpringAnimation是iOS9新加入动画类型,是CABasicAnimation的子类,用于实现弹簧动画。</p> <p>CASpringAnimation的重要属性:</p> <p>mass:质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)</p> <p>stiffness:弹性系数(弹性系数越大,弹簧的运动越快)</p> <p>damping:阻尼系数(阻尼系数越大,弹簧的停止越快)</p> <p>initialVelocity:初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)</p> <p>settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)</p> <p>CASpringAnimation和UIView的SpringAnimation对比:</p> <p>1.CASpringAnimation 可以设置更多影响弹簧动画效果的属性,可以实现更复杂的弹簧动画效果,且可以和其他动画组合。</p> <p>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 style="text-align:center"><img src="https://simg.open-open.com/show/342e58924429de861df889218320428d.gif"></p> <p style="text-align:center">springAni.gif</p> <h2>CAAnimationGroup</h2> <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 style="text-align:center"><img src="https://simg.open-open.com/show/6b5b1c53f26619ca692953879b57d7f1.gif"></p> <p style="text-align:center">groupAni.gif</p> <h2>CATransaction</h2> <p>最后讲一下事务,在核心动画里面存在事务(CATransaction)这样一个概念,它负责协调多个动画原子更新显示操作。</p> <p>简单来说事务是核心动画里面的一个基本的单元,动画的产生必然伴随着layer的Animatable属性的变化,而layer属性的变化必须属于某一个事务。</p> <p>因此,核心动画依赖事务。</p> <p>事务的作用:保证一个或多个layer的一个或多个属性变化同时进行</p> <p>事务分为隐式和显式:</p> <p>1.隐式:没有明显调用事务的方法,由系统自动生成事务。比如直接设置一个layer的position属性,则会在当前线程自动生成一个事务,并在下一个runLoop中自动commit事务。</p> <p>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才有隐式动画,如果你是直接设置self.cartCenter.layer.bounds = self.centerShow.layer.bounds;是不会有动画效果的。</p> <p>via: http://www.cocoachina.com/ios/20160517/16290.html</p>