给iOS图层添加动画

作者: 熊妹妹 | 来源:发表于2017-03-24 16:44 被阅读498次

核心动画可以在layer上创造复杂的动画,比如修改大小、位置、旋转变换等等,也可以同时修改一个或多个属性。

简单修改layer属性做动画

首先理解下隐式动画和显示动画。当修改图层任意属性时都会出发隐式动画,隐式动画有默认时间步调和其他属性。如果不使用系统的默认动画可以通过创建CABasicAnimation对象指定图层的显示动画。调用addAnimation:forKey:方法把动画添加到图层上

在之前的矢量图上添加一个缩放的动画

CAShapeLayer *layer1 = [CAShapeLayer layer];
layer1.bounds = CGRectMake(0, 0, 200, 200);
layer1.position = self.view.center;
layer1.backgroundColor = [UIColor colorWithRed:0.9 green:0.3 blue:0.3 alpha:0.3].CGColor;
    
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 0)];
[path addCurveToPoint:CGPointMake(0, 50)
       controlPoint1:CGPointMake(50, 25)
       controlPoint2:CGPointMake(25, 50)];
    
[path addCurveToPoint:CGPointMake(-50, 0)
       controlPoint1:CGPointMake(-25, 50)
       controlPoint2:CGPointMake(-50, 25)];
    
[path addCurveToPoint:CGPointMake(0, -50)
       controlPoint1:CGPointMake(-50, -25)
       controlPoint2:CGPointMake(-25, -50)];
    
[path addCurveToPoint:CGPointMake(50, 0)
       controlPoint1:CGPointMake(25, -50)
       controlPoint2:CGPointMake(50, -25)];
CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
[path applyTransform:transform];
    
layer1.path = path.CGPath;
layer1.fillColor = [UIColor whiteColor].CGColor;
layer1.strokeColor = [UIColor blueColor].CGColor;
    
[self.view.layer addSublayer:layer1];
    
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 0.2, 1)];
animation.duration = 4.0;
[layer1 addAnimation:animation forKey:@"animation1"];

<img src="https://img.haomeiwen.com/i1840221/e8152fe11bd3915a.gif?imageMogr2/auto-orient/strip" width=250>

要注意,显式动画只是提供一个动画,它并不会修改图层对象的数据。所以在动画结束时,需要更新图层对象的数据。

动画会在runloop结束时开始执行,因此当前线程必须要有一个runloop才可以让动画执行。即便改变图层的多个属性或是添加多个动画,他们都会在同一时刻执行。比如你可以在移动的时候同时修改图层的不透明度。虽然他们是同时执行的,但是你可以单独配置每个属性的动画对象,控制他们属性变化的时间节奏。

用关键帧动画修改layer属性

关键帧动画可以在一个动画周期内给属性设置多个值。关键帧动画由一系列的值和每两个值之间变化时的时间节奏组成,他们都用数组表示。

CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
keyframeAnimation.keyTimes = @[@(0),@(0.3),@(0.5),@(1)];
keyframeAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 1.2, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 0.2, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.3, 1.2, 1)]];
keyframeAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
keyframeAnimation.duration = 8.f;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
[layer1 addAnimation:keyframeAnimation forKey:@"transform2"];

<img src="https://img.haomeiwen.com/i1840221/e58e4631a46e29fd.gif?imageMogr2/auto-orient/strip" width=250>

动画属性keyPath

在第一行创建关键帧动画时调用方法animationWithKeyPath,并传入动画属性keypath。CAPropertyAnimation定义了动画的属性值,它支持很多属性,具体可以查阅官方文档。

关键帧的值

通过动画的path或values来设置关键帧的值。属性是CGPoint类型的(锚点或位置)通常用动画的path属性来设置,path是CGPathRef类型。也可以用values属性来设置,它是数组类型。这时需要把CGPoint类型用NSValue来进行封装再添加到数组中,同样的CATransform3D和CGRect类型用NSValue封装、 CGFloat类型用NSNumber封装。

关键帧之间的节奏

关键帧动画的时间节奏和插值是通过一系列属性共同控制的:

  • calculationMode:动画中的计算算法,这个属性同时影响其他属性的使用
    • kCAAnimationLinear 或 kCAAnimationCubic,关键帧之间使用直线或圆滑曲线的插值算法实现。 当设置为kCAAnimationCubic时,可通过参数调整差值曲线函数来完全控制中间帧变化方法。
    • kCAAnimationPaced or kCAAnimationCubicPaced,使keyTimes和timingFunctions属性失效。动画平滑过渡,也就是匀速进行。
    • kCAAnimationDiscrete,使timingFunctions属性失效。动画在关键帧之间跳跃进行,也就是中间帧不进行差值计算,直接从两个关键帧变化。
  • keyTimes 指定每一个关键帧的属性值
  • timingFunctions 指定每两个关键帧之间过度的时间曲线变化

动画协议

你会发现第一个demo动画结束时,layer回到了初始状态,而第二个demo在开始和结束时layer保持动画开始和结束的状态。因为我设置了fillMode和removedOnCompletion这两个属性。layer的transform值始终都是CATransform3DIdentity,但是设置了这两个属性后可以让图层动画开始时就渲染到CATransform3DMakeScale(1.5, 1.5, 1),结束时渲染到CATransform3DMakeScale(0.3, 1.2, 1)。

CAMediaTiming协议是一套抽象接口,描述了动画的一些信息,CALayer和CAAnimation都实现了该协议

  • beginTime 相对于父图层或动画的开始时间
  • duration 动画的单次时长
  • speed 动画的播放速率,默认是1
  • timeOffset 动画的时间偏移,就是从哪里开始播放,默认是0
  • repeatCount 动画的重复播放次数,默认是0
  • repeatDuration 动画的有效时长,默认是0。结合重复次数使用
  • autoreverses 如果是true,动画正播后再进行反播。默认是false
  • fillMode 动画的填充模式,这就是上面用到的。可以四种情况:渲染开始和结束维持layer的属性值、在动画结束时layer渲染到动画结束时的属性、在动画开始时layer渲染到动画开始时的属性、开始结束都渲染。

removedOnCompletion是CAAnimation的属性,它表示动画一旦结束渲染就停止,所以第一个demo看到动画结束时layer又变回了初始的样子。

运行时停止动画

动画通常会一直运行到结束,如果想提前中止有两种方法:

  • 移除一个动画调用图层的removeAnimationForKey:方法,参数和添加动画时addAnimation:forKey:的第二个参数相同,都不能为nil
  • 移除全部动画调用图层的removeAllAnimations方法,它会马上中止所有正在进行的动画,并对layer进行重绘。但这个方法最好不要用!

当从图层移除一个动画的时候,会出发图层的重新绘制。图层会跳跃到动画之前状态,如果想保持最后一帧的状态可以通过修改图层属性实现。

动画组合

如果想图层一直执行多个动画,可以创建CAAnimationGroup对象通过配置animations属性来实现。动画组的的事件节奏和时长属性会覆盖掉所包含的动画属性。

检测动画结束

当动画开始和结束时,可以通过回调得知:

  • 对动画事物CATransaction添加setCompletionBlock:方法,在动画结束时调用
  • 动画对象实现animationDidStart:和animationDidStop:finished:方法

如果你想连接两个动画尽量不要用这种方式,通过设置动画的beginTime来实现。

相关文章

网友评论

    本文标题:给iOS图层添加动画

    本文链接:https://www.haomeiwen.com/subject/fefxottx.html