核心动画的继承关系图
一、基础动画
改变位移、透明度、缩放、旋转、背景色改变等行为产生的动画,系统CALyer
的核心动画的有些Api在改变指定属性后默认就能产生动画,只有非root layer才有隐式动画,进入头文件查看凡是有标注Animatable
的Api默认都可以产生动画,例如下面的position
属性:
下面分别使用三种方式实现位移动画为样例,这几种虽然效果都是一样的,但是使用不同的系统API会有不同的动画属性和状态;其他的动画用法都一样,只需要修改keyPath
和fromValue
、toValue
的值即可。
- 实现方式一:使用
CAlayer
的核心动画实现方式
//使用CABasicAnimation创建基础动画
-(void)testAnimation
{
CABasicAnimation *anima = [CABasicAnimation animation];
anima.keyPath = @"position"; //整体平移
//anima.keyPath = @"position.x"; //则x方向平移多少
//anima.keyPath = @"transform.translation; //也是平移动画
//动画从一个点执行到另一个点,记得这里接收的值要包装成 `NSValue` 。。。
anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-75)];
anima.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-75)];
//在原来值的基础上增加多少
//anima.byValue =@(80.0);
//动画执行时间
anima.duration = 1.0f;
//设置动画保持结束的状态,注意:实际上图层的属性值还是动画执行前的初始值,并没有真正被改变。
anima.fillMode = kCAFillModeForwards;
anima.removedOnCompletion = NO;
//动画执行次数,`MAXFLOAT`表示最大次数。
anima.repeatCount = MAXFLOAT;
//设置当前为实例为动画的代理<CAAnimationDelegate>,可监听动画的开始和结束。
anima.delegate = self;
//控制动画执行的速度节奏,具体有哪些值可进入头文件查看。
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[_demoView.layer addAnimation:anima forKey:@"positionAnimation"];
}
1.1 CAPropertyAnimation
中KeyPath
的值可以有以下这些
transform.scale = 比例转换
transform.rotation = 旋转
opacity = 透明度
margin = 边距
position = 位移
backgroundColor = 背景颜色
cornerRadius = 圆角
borderWidth = 边框宽度
bounds = 位置,体积
contents = 内容
contentsRect = 面积
frame = 位置,体积
hidden = 是否隐藏
mask = 任务
masksToBounds
shadowColor = 阴影颜色
shadowOffset = 阴影偏移
shadowOpacity = 阴影透明
shadowRadius = 阴影半径
1.2 这里的keyPath
能设置的值在系统API中的有这些,可以在Xcode的苹果官方文档中键入关键字CATransform3D value key paths
搜索能看到详细的用法:
2 . 实现方式二: 使用UIView的Block动画实现方式
//使用UIView Animation 代码块调用
-(void)testAnimation2
{
[UIView animateWithDuration:1.0f animations:^{
//平移动画
_demoView.transform = CGAffineTransformMakeTranslation(SCREEN_WIDTH, SCREEN_HEIGHT/2-50);
} completion:^(BOOL finished) {
//复原transform值,复原也会使控件位置复原
_demoView.transform = CGAffineTransformIdentity;
}];
}
3 . 实现方式三: 使用UIVew的【begin commit 】这种用法在开发中使用较少
//使用UIView [begin,commit]模式
-(void)testAnimation3
{
_demoView.frame = CGRectMake(0, SCREEN_HEIGHT/2-50, 50, 50);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0f];
_demoView.frame = CGRectMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50, 50, 50);
[UIView commitAnimations];
}
** 对比总结:** 这里三种方式都可以实现相同的动画,不同的实现方式有不同的好处,在我们项目中可以根据自己的实际需求选择相应的实现方式,建议是:在对UI控件的大小位置有没有要求的情况下我们推荐使用CALayer
的核心动画实现,没有要求的可以使用UIView
的Block
方式实现;而核心动画都是后台执行的动画它不会阻塞UI线程,能执行很多绚丽的组合动画,但也有一些弊端,比如动画在页面之间切换、前后台的切换后动画有可能会停止,这些弊端在使用UIView
的Block
方式就不会有这样的问题,相应的UIView
动画能执行的动画类型相对少很多。
二、帧动画
关键帧动画CAKeyframeAnimation
类继承于CAPropertyAnimation
类,属于属性动画的范畴,它能做一组相同的动画,因此它有一个values
数组Api属性,数组中都是每个关键帧所做的动画值,常见的关键帧动画比如做平移、图标抖动等动画。
1 . 一组移动位置的 关键帧动画用法例子:
/**
* 关键帧动画
*/
-(void)keyFrameAnimation{
CAKeyframeAnimation *anima = [CAKeyframeAnimation animation];
anima.keyPath = @"position"; //关键帧平移动画
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
//每个动画的值,需要包装成`NSValue`类型
anima.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
anima.duration = 2.0f;
//设置动画的节奏
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
//设置代理,可以检测动画的开始和结束
anima.delegate = self;
[_demoView.layer addAnimation:anima forKey:@"keyFrameAnimation"];
}
2 . 关键帧动画CAKeyframeAnimation
类还有一个CGPathRef
类型的path
属性,因此也可以做一些围绕路径的帧动画,例如下面的path
转圈动画:
/**
* path动画
*/
-(void)pathAnimation{
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
anima.path = path.CGPath;
anima.duration = 2.0f;
[_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
}
三、转场动画
1 . 实现方式一:使用transitionFromView: toView: duration:...
此Api的转过渡场动画是发生在两个View
之间, 官方文档上明确提出: 在动画完毕之后, fromView
会出从父视图上被移除, 而 toView
会被添加到 fromView
所在的父视图,因此在做系统的反转动画时,需要一个自定义的父视图View
,如果直接使用控制器的View
,则会把当前控制器的View
一并反转。 代码例子如下:
- (void)transitionAnimation1
{
self.goingToFront = !self.goingToFront;
UIView *fromView = self.goingToFront ? self.imageView1 : self.imageView2;
UIView *toView = self.goingToFront ? self.imageView2 : self.imageView1;
UIViewAnimationOptions transitionDirection =
self.goingToFront ? UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft;
[UIView transitionFromView:fromView
toView:toView
duration:0.5
options:transitionDirection
completion:^(BOOL finished) {
NSLog(@"动画完成\n===%@\n===%@\n===%@",self.imageView1,self.imageView2,self.contentView);
}];
}
2 . 实现方式二:使用transitionWithView: duration:...
此Api这种类型的转场动画是发生在一个View
之间的过渡转场,因为参数就只传入一个UIView
. 代码例子如下:
- (void) transitionAnimation2
{
self.goingToFront = !self.goingToFront;
UIViewAnimationOptions transitionDirection =
self.goingToFront ? UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft;
[UIView transitionWithView:self.contentView
duration:0.5
options:transitionDirection
animations:^{
[self.contentView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
} completion:^(BOOL finished) {
NSLog(@"动画完成");
}];
}
3 . 实现方式三:使用CATransition
此Api的核心动画 . 代码例子如下:
-(void) transitionAnimation3{
CATransition *anima = [CATransition animation];
//设置动画的类型
anima.type = kCATransitionPush;
//设置动画的方向
anima.subtype = kCATransitionFromRight;
//动画持续时间
anima.duration = 1.0f;
//设置动画起始点
anima.startProgress = 0.3;
//设置动画结束点
anima.endProgress = 0.8;
[_demoView.layer addAnimation:anima forKey:@"pushAnimation"];
}
3.1 这里的anima.type
动画的类型: 可选的动画类型非常有限,系统默认提供了四个值:
kCATransitionFade; //逐渐消失
kCATransitionMoveIn; //逐渐进入
kCATransitionPush; //类似导航控制器的push动画
kCATransitionReveal; //逐渐移出
3.2 除系统默认提供的动画类型之外,还有一些私有Api的动画类型,不过使用这些私有Api会有上架被拒风险,具体的类型如下:
Fade = 1, //淡入淡出
Push, //推挤
Reveal, //揭开
MoveIn, //覆盖
Cube, //立方体
SuckEffect, //吮吸
OglFlip, //翻转
RippleEffect, //波纹
PageCurl, //翻页
PageUnCurl, //反翻页
CameraIrisHollowOpen, //开镜头
CameraIrisHollowClose, //关镜头
CurlDown, //下翻页
CurlUp, //上翻页
FlipFromLeft, //左翻转
FlipFromRight, //右翻转
3.3 anima.subtype
动画的方向:系统默认也提供了四个值:
kCATransitionFromRight; //从右边开始
kCATransitionFromLeft; //从左边开始
kCATransitionFromTop; //从上面开始
kCATransitionFromBottom; //从下面开始
四、组合动画
CAAnimation
的子类,可以保存一组动画对象,将CAAnimationGroup
对象加入层后,组中所有动画对象可以同时并发运行。重要属性:animations
用来保存一组动画对象的NSArray
一个同时执行的组合动画例子:
/**
* 组合动画
*/
-(void)groupAnimation{
//位移动画
CAKeyframeAnimation *anima1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
anima1.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
//缩放动画
CABasicAnimation *anima2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anima2.fromValue = [NSNumber numberWithFloat:0.8f];
anima2.toValue = [NSNumber numberWithFloat:2.0f];
//旋转动画
CABasicAnimation *anima3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
anima3.toValue = [NSNumber numberWithFloat:M_PI*4];
//组动画
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = [NSArray arrayWithObjects:anima1,anima2,anima3, nil];
groupAnimation.duration = 4.0f;
[_demoView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
}