App如果想被大众喜欢,漂亮的UI和精美的动画都是必不可少的,苹果虽然为UIView提供了一些常用动画,但是大部分看起来比较不错的效果都是通过操作Layer层实现的,因此了解核心动画是必要的。CoreAnimation是直接作用在CALayer上的(并非UIView上)非常强大的跨Mac OS X和iOS平台的动画处理API,Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。
介绍
先来看一下比较常见的层次图:
核心动画的动画类都是从CAAnimation类继承而来,先来看一下CAAnimation的层次结构:
CAAnimation实现了CAMediaTiming协议(提供了动画的持续时间,速度,和重复计)和CAAction协议(为图层触发一个动画动作提供了提供标准化响应)。
CAPropertyAnimation是CABasicAnimation和CAKeyframeAnimation的抽象父类,CABasicAnimation为图层的属性提供修改,实现起来比较简单;CAKeyframeAnimation支持关键帧动画,你可以指定的图层属性的关键路径动画,包括动画的每个阶段的价值,以及关键帧时间和计时功能的一系列值。
CAAnimationGroup允许一系列动画效果组合在一起,并行显示动画。
CATransition提供了一个图层变化的过渡效果,它能影响图层的整个内容。动画进行的时候淡入淡出(fade)、推(push)、显露(reveal)图层的内容。这些过渡效果可以扩展到你自己定制的 Core Image 滤镜。
动画类同时定义了一个使用贝塞尔曲线来描述动画改变的时间函数。匀速时间函数(lineartiming function)速度会一直保持不变,渐缓时间函数(ease-outtiming function)则在动画接近其生命周期的时候减慢速度。
CABasicAnimation
CoreAnimation中最简单的莫过于CABasicAnimation,只需要设置fromValue和toValue即可,先来看一段简单的代码:
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotationAnimation.fromValue = [NSNumber numberWithFloat:0.0];
rotationAnimation.toValue = [NSNumber numberWithFloat:2*M_PI];
rotationAnimation.repeatCount = MAXFLOAT;
rotationAnimation.duration =10;
[self.rotationImgView.layer addAnimation:rotationAnimation forKey:@"transform.rotation"];
动画会让图片不断的旋转,效果如下:
相对应的我们也可以按照X轴和Y轴进行旋转:
self.rotationXImgView.clipsToBounds=YES;
self.rotationXImgView.layer.cornerRadius=CGRectGetWidth(self.rotationXImgView.frame)/2;
CABasicAnimation *rotationXAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
rotationXAnimation.fromValue = [NSNumber numberWithFloat:0.0];
rotationXAnimation.toValue = [NSNumber numberWithFloat:2*M_PI];
rotationXAnimation.repeatCount = MAXFLOAT;
rotationXAnimation.duration =3;
[self.rotationXImgView.layer addAnimation:rotationXAnimation forKey:@"transform.rotation.x"];
self.rotationYImgView.clipsToBounds=YES;
self.rotationYImgView.layer.cornerRadius=CGRectGetWidth(self.rotationYImgView.frame)/2;
CABasicAnimation *rotationYAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotationYAnimation.fromValue = [NSNumber numberWithFloat:0.0];
rotationYAnimation.toValue = [NSNumber numberWithFloat:2*M_PI];
rotationYAnimation.repeatCount = MAXFLOAT;
rotationYAnimation.duration =10;
[self.rotationYImgView.layer addAnimation:rotationYAnimation forKey:@"transform.rotation.y"];
Scale放大效果:
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.5];
scaleAnimation.autoreverses = YES;//自动反向执行动画效果
scaleAnimation.repeatCount = MAXFLOAT;
scaleAnimation.duration = 0.8;
[self.scaleImgView.layer addAnimation:scaleAnimation forKey:@"FlyElephant.scale"];
CABasicAnimation *scaleXAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
scaleXAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleXAnimation.toValue = [NSNumber numberWithFloat:1.5];
scaleXAnimation.autoreverses = YES;//自动反向执行动画效果
scaleXAnimation.repeatCount = MAXFLOAT;
scaleXAnimation.duration = 0.8;
[self.scaleXImgView.layer addAnimation:scaleXAnimation forKey:@"FlyElephant.scale.x"];
CABasicAnimation *scaleYAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
scaleYAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleYAnimation.toValue = [NSNumber numberWithFloat:1.5];
scaleYAnimation.autoreverses = YES;//自动反向执行动画效果
scaleYAnimation.repeatCount = MAXFLOAT;
scaleYAnimation.duration = 0.8;
[self.scaleYImgView.layer addAnimation:scaleYAnimation forKey:@"FlyElephant.scale.y"];
CABasicAnimation *scaleZAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.z"];
scaleZAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleZAnimation.toValue = [NSNumber numberWithFloat:1.5];
scaleZAnimation.autoreverses = YES;//自动反向执行动画效果
scaleZAnimation.repeatCount = MAXFLOAT;
scaleZAnimation.duration = 0.8;
[self.scaleZImgView.layer addAnimation:scaleZAnimation forKey:@"FlyElephant.scale.z"];
Translation平移效果
CABasicAnimation *translationX=[CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
translationX.toValue=@(200);
translationX.duration=5;
translationX.removedOnCompletion=NO;
translationX.fillMode=kCAFillModeForwards;
translationX.repeatCount=MAXFLOAT;
translationX.autoreverses=YES;
[self.translationXImgView.layer addAnimation:translationX forKey:@"FlyElephant.translation.x"];
CABasicAnimation *translationY=[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
translationY.toValue=@(100);
translationY.duration=5;
translationY.removedOnCompletion=NO;
translationY.fillMode=kCAFillModeForwards;
translationY.repeatCount=MAXFLOAT;
translationY.autoreverses=YES;
[self.translationYImgView.layer addAnimation:translationY forKey:@"FlyElephant.translation.y"];
CABasicAnimation *translation=[CABasicAnimation animationWithKeyPath:@"transform.translation"];
translation.toValue=[NSValue valueWithCGPoint:CGPointMake(100, 100)];
translation.duration=5;
translation.removedOnCompletion=NO;
translation.fillMode=kCAFillModeForwards;
translation.repeatCount=MAXFLOAT;
translation.autoreverses=YES;
[self.translationImgView.layer addAnimation:translation forKey:@"FlyElephant.translation"];
CABasicAnimation设置比较简单,如果有心的话会有一个疑问,keyPath的里面设置的值是怎么找到的,怎么可以找到所有animationWithKeyPath中的keyPath需要的值呢?最后总结的时候会给出答案,如果有兴趣的可以自己先搜索一番~
CAKeyframeAnimation
CAKeyframeAnimation也被称为关键帧动画,因为它可以细化动画在运动过程中的变换的位置,路径以及时间,如果设置的比较好可以完成很多意想不到的效果,本文就简单的稍微设置一下,之后有时间会单独补充一些有意思的动画:
基于Point的变换
CGPoint p1=CGPointMake(self.positionImgView.center.x, self.positionImgView.center.y);
CGPoint p2=CGPointMake(80, 100);
CGPoint p3=CGPointMake(100, 120);
CGPoint p4=CGPointMake(120, 150);
CGPoint p5=CGPointMake(140, 160);
NSArray *values=[NSArray arrayWithObjects:[NSValue valueWithCGPoint:p1],[NSValue valueWithCGPoint:p2],[NSValue valueWithCGPoint:p3],[NSValue valueWithCGPoint:p4],[NSValue valueWithCGPoint:p5], nil];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
[animation setValues:values];
[animation setDuration:10.0];
[animation setCalculationMode:kCAAnimationCubic];
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[self.positionImgView.layer addAnimation:animation forKey:@"FlyElephant.point"];
基于Path变换
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path,NULL,self.positionImgView.center.x,self.positionImgView.center.y);
for (NSInteger i=1; i<5; i++) {
CGPathAddLineToPoint(path, NULL, self.positionImgView.center.x+i*10, self.positionImgView.center.y);
}
//曲线
CGPathAddCurveToPoint(path,NULL,50.0,275.0,150.0,275.0,70.0,120.0);
CGPathAddCurveToPoint(path,NULL,150.0,275.0,250.0,275.0,90.0,120.0);
CGPathAddCurveToPoint(path,NULL,250.0,275.0,350.0,275.0,110.0,120.0);
CGPathAddCurveToPoint(path,NULL,350.0,275.0,450.0,275.0,130.0,120.0);
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
[animation setPath:path];
[animation setDuration:3.0];
// [animation setAutoreverses:YES];
CFRelease(path);
[self.positionImgView.layer addAnimation:animation:@"FlyElephant"];
通过代码我们发现,Path和values接收都是一个数组,而不是一个固定值,这里面我们没有设置keyTimes,下面看一个常见的抖动效果:
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position.x";
animation.values = @[ @0, @20, @-20, @20, @0 ];
animation.keyTimes = @[ @0, @(1 /8.0), @(1/ 2.0), @(7/ 8.0), @1 ];
animation.duration =0.5;
animation.additive = YES;
[self.textField.layer addAnimation:animation forKey:@"FlyElephant.Shake"];
CAAnimationGroup
CAAnimationGroup被称为动画组,动画组可以将各种不同的动画同时执行,如果你想,你就可以设置千变万化的效果,下面简单的将CABasicAnimation和CAKeyframeAnimation结合在一起做一个简单的动画效果:
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:2.0];
NSMutableArray *arr=[NSMutableArray array];
for (NSInteger i=0; i<5; i++) {
CGPoint p=CGPointMake(self.groupImgView.center.x+i*20, self.groupImgView.center.y+i*20);
[arr addObject:[NSValue valueWithCGPoint:p]];
}
NSArray *values=arr;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
[animation setValues:values];
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
CAAnimationGroup *animationGroup=[CAAnimationGroup animation];
animationGroup.animations=@[scaleAnimation,animation];
animationGroup.duration=5.0f;
animationGroup.autoreverses=YES;
[self.groupImgView.layer addAnimation:animationGroup forKey:@"FlyElephant.AnimationGroup"];
这个算是基本动画,需求不一样,设置的效果就不一样~
AnimationWithKeyPath的值
最开始做动画一般都会对keyPath这个值莫名其妙,因为它不是常量,需要变换的时候找不到对应的需要设置的值,如果你在网上搜索,很可能看到的是这张图:
下面这张图你基本上是找不到的,如下:
这两张图涵盖所有你需要设置的keypath,两张图都是截取自苹果官网,自己当时找的时候也发费了一番功夫,参考链接如下:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/AnimatableProperties/AnimatableProperties.html#//apple_ref/doc/uid/TP40004514-CH11-SW2
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreAnimation_guide/Key-ValueCodingExtensions/Key-ValueCodingExtensions.html#//apple_ref/doc/uid/TP40004514-CH12-SW2
Swift 抖动效果
let animation:CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0.1
animation.toValue = -0.1
animation.duration = 0.1;
animation.autoreverses = true; //是否重复
animation.repeatCount = 2;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
self.shakeView.layer.add(animation, forKey: "shake")