一、Core Animation
1、属性动画
1.1 基础动画
1.2 关键帧动画
2、过渡动画
3、动画组
二、UIView Animation
1、属性动画
1.1 基础动画
1.2 关键帧动画
2、过渡动画
3、约束动画需注意
三、Core Animation和UIView Animation怎么选择
一、Core Animation
iOS核心动画高级技巧
Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常绚丽的动画效果,而且往往是事半功倍,也就是说使用少量的代码就可以实现非常强大的功能。
需要注意的是,Core Animation是直接作用在CALayer层的,并非UIView层;并且Core Animation的动画执行过程都是在子线程的,也就是说异步的,不会阻塞主线程,所以实际开发中我们要注意所做的事情是否真得是等动画结束了。
1、属性动画
所谓属性动画,就是作用于CALayer属性的动画,CALayer常见的可动画属性有:
- 最普通的属性:backgroundColor、frame、bounds、position
- 显隐性属性:opacity、hidden
- 仿射变换和3D仿射变换属性:affineTransform(仿射变换)、transform(3D仿射变换)
- 切圆角、设置边框和设置阴影属性:cornerRadius、masksToBounds、borderColor、borderWidth、shadowColor、shadowOffset、shadowOpacity、shadowRadius
UIView和CALayer的仿射变换属性主要就是两个用途:一和动画配合,二和手势配合。
实际开发中我们最常见的就是对仿射变换属性做动画了,比如平移动画我们会用仿射变换的CATransform3DMakeTranslation来实现、而非frame或position,比如缩放动画我们会用仿射变换的CATransform3DMakeScale来实现、而非frame或bounds,旋转会用仿射变换的CATransform3DMakeRotation来实现。
1.1 基础动画
基础动画是属性动画的一个子类,所以它也是作用于CALayer属性的动画。所谓基础动画,就是给定属性的一个开始值和一个结束值,再给定一个动画时间,那么在指定的时间内,该属性就会从开始值变化到结束值。
基础动画的使用很简单,只需要按下面7步来做就可以了:
- 创建一个基础动画
- 设置动画要作用的属性
- 设置属性的初始值(也可以不设置,不设置的话默认就是创建CALayer时的值)
- 设置属性的结束值
- 设置动画时长、动画重复次数等配置
- 如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
- 把这个动画添加到layer上,动画就会开始执行了
现在我们写个例子看看,给customLayer添加一个背景色变化(红色 -> 橙色)的动画和一个平移动画((100, 100) -> (200, 200))。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *customLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customLayer = [[CALayer alloc] init];
self.customLayer.backgroundColor = [UIColor redColor].CGColor;
self.customLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.customLayer];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 1、创建一个基础动画
CABasicAnimation *basicAnimation = [[CABasicAnimation alloc] init];
// 2、设置动画要作用的属性
basicAnimation.keyPath = @"backgroundColor";
// 3、设置属性的初始值
basicAnimation.fromValue = (id)[UIColor redColor].CGColor;
// 4、设置属性的结束值
basicAnimation.toValue = (id)[UIColor orangeColor].CGColor;
// 5、设置动画时长、动画重复次数等配置
basicAnimation.duration = 3;
basicAnimation.repeatCount = MAXFLOAT; // 0和1代表不重复,MAXFLOAT代表无限重复
basicAnimation.autoreverses = YES; // 自动反转:动画重复执行时,下一次动画开始执行的状态要不要使用上一次动画的结束执行的状态,默认每一次动画开始执行时都是初始状态
basicAnimation.removedOnCompletion = NO; // 动画执行结束后,不要移除动画
basicAnimation.fillMode = kCAFillModeForwards; // 动画执行结束后,让动画保持最新的状态,而不要跳回初始的状态(前提是“动画执行结束后,不要移除动画”)
// 6、如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
basicAnimation.delegate = self;
// 7、把这个动画add到layer上,动画就会开始执行了
[self.customLayer addAnimation:basicAnimation forKey:nil];
CABasicAnimation *basicAnimation1 = [[CABasicAnimation alloc] init];
basicAnimation1.keyPath = @"transform";
basicAnimation1.fromValue = (id)[NSValue valueWithCATransform3D:(CATransform3DMakeTranslation(0, 0, 0))];
basicAnimation1.toValue = (id)[NSValue valueWithCATransform3D:(CATransform3DMakeTranslation(100, 100, 0))];
basicAnimation1.duration = 3;
basicAnimation1.repeatCount = MAXFLOAT;
basicAnimation1.autoreverses = YES;
basicAnimation1.removedOnCompletion = NO;
basicAnimation1.fillMode = kCAFillModeForwards;
basicAnimation1.delegate = self;
[self.customLayer addAnimation:basicAnimation1 forKey:nil];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim {
NSLog(@"动画开始了");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(@"动画结束了");
}
@end
1.2 关键帧动画
关键帧动画也是属性动画的一个子类,所以它也是作用于CALayer属性的动画。所谓关键帧动画,就是给定属性的若干个离散的值甚至是一条路径上无数个连续的值,而非仅仅给定一个开始值和一个结束值,当然这若干个离散的值或一条路径上无数个连续的值里肯定包含一个开始值和一个结束值,再给定一个动画时间,那么在指定的时间内,该属性就会从开始值历经其它值变化到结束值。
关键帧动画的使用也很简单,只需要按下面6步来做就可以了:
- 创建一个关键帧动画
- 设置动画要作用的属性
- 设置属性的若干个离散的值(注意第一个关键帧不能省略,它不会默认为创建CALayer时的值,我们需要主动设置为创建CALayer时的值)或一条路径上无数个连续的值
- 设置动画时长、动画重复次数等配置
- 如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
- 把这个动画添加到layer上,动画就会开始执行了
现在我们写个例子看看,给customLayer添加一个背景色变化(红色 -> 橙色 -> 黄色 -> 绿色 -> 青色 -> 蓝色 -> 紫色)的动画和一个平移动画(沿着一条三次贝塞尔曲线)。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *customLayer;
@property (nonatomic, strong) UIBezierPath *path;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customLayer = [[CALayer alloc] init];
self.customLayer.backgroundColor = [UIColor redColor].CGColor;
self.customLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.customLayer];
// 绘制一个三次贝塞尔曲线
self.path = [[UIBezierPath alloc] init];
[self.path moveToPoint:CGPointMake(150, 150)];
[self.path addCurveToPoint:CGPointMake(150, 450) controlPoint1:CGPointMake(250, 250) controlPoint2:CGPointMake(50, 350)];
// 通过shapelayer把贝塞尔曲线显示出来,
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = self.path.CGPath;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 1、创建一个关键帧动画
CAKeyframeAnimation *keyframeAnimation = [[CAKeyframeAnimation alloc] init];
// 2、设置动画要作用的属性
keyframeAnimation.keyPath = @"backgroundColor";
// 3、设置属性的若干个离散的值
keyframeAnimation.values = @[
(id)[UIColor redColor].CGColor, // 第一帧,开始值
(id)[UIColor orangeColor].CGColor,
(id)[UIColor yellowColor].CGColor,
(id)[UIColor greenColor].CGColor,
(id)[UIColor cyanColor].CGColor,
(id)[UIColor blueColor].CGColor,
(id)[UIColor purpleColor].CGColor, // 最后一帧,结束值
];
// 4、设置动画时长、动画重复次数等配置
keyframeAnimation.duration = 3;
keyframeAnimation.removedOnCompletion = NO; // 动画执行结束后,不要移除动画
keyframeAnimation.fillMode = kCAFillModeForwards; // 动画执行结束后,让动画保持最新的状态,而不要跳回初始的状态(前提是“动画执行结束后,不要移除动画”)
// 5、如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
keyframeAnimation.delegate = self;
// 6、把这个动画add到layer上,动画就会开始执行了
[self.customLayer addAnimation:keyframeAnimation forKey:nil];
CAKeyframeAnimation *keyframeAnimation1 = [[CAKeyframeAnimation alloc] init];
keyframeAnimation1.keyPath = @"position";
keyframeAnimation1.path = self.path.CGPath; // 设置属性的一条路径上无数个连续的值
keyframeAnimation1.duration = 3;
keyframeAnimation1.removedOnCompletion = NO;
keyframeAnimation1.fillMode = kCAFillModeForwards;
keyframeAnimation1.rotationMode = kCAAnimationRotateAuto; // 让customLayer根据曲线自动旋转方向
keyframeAnimation1.delegate = self;
[self.customLayer addAnimation:keyframeAnimation1 forKey:nil];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim {
NSLog(@"动画开始了");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(@"动画结束了");
}
@end
2、过渡动画
上面的属性动画是用来作用于CALayer的可动画属性的,而这里的过渡动画则是用来作用于CALayer的不可动画属性的,比如label.layer上显示的文本,imageView.layer上显示的图片等。所谓过渡动画,就是首先展示修改前的图层外观,然后设置修改后的图层外观,再给定一个动画类型和动画时间,那么在指定的时间内,图层外观就会通过一个过渡动画的效果改变到新的外观展示。
过渡动画的使用很简单,只需要按下面5步来做就可以了:
- 创建一个过渡动画
- 设置修改后的图层外观
- 设置动画类型、子类型、时间曲线、动画时长、动画重复次数等配置
- 如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
- 把这个动画添加到layer上,动画就会开始执行了
现在我们写个例子看看,给label.layer上显示的文本和imageView.layer上显示的图片的变化添加过渡动画。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] init];
self.imageView.frame = CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.width);
// 首先展示修改前的图层外观
self.imageView.image = [UIImage imageNamed:@"乔丹.png"];
[self.view addSubview:self.imageView];
self.label = [[UILabel alloc] init];
self.label.frame = CGRectMake(0, self.view.bounds.size.width + 20, self.view.bounds.size.width, 30);
// 首先展示修改前的图层外观
self.label.text = @"乔丹";
self.label.textColor = [UIColor blackColor];
self.label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:self.label];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 1、创建一个过渡动画
CATransition *transition = [[CATransition alloc] init];
// 3、设置动画类型、子类型、时间曲线、动画时长、动画重复次数等配置
transition.type = @"pageCurl";
transition.subtype = kCATransitionFromTop;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.duration = 3;
// 2、设置修改后的图层外观
self.imageView.image = [UIImage imageNamed:@"科比.png"];
self.label.text = @"科比";
// 4、如果我们想监听动画的执行过程,可以设置代理,通过代理方法来监听
transition.delegate = self;
// 5、把这个动画添加到layer上,动画就会开始执行了
[self.imageView.layer addAnimation:transition forKey:nil];
[self.label.layer addAnimation:transition forKey:nil];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim {
NSLog(@"动画开始了");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(@"动画结束了");
}
@end
过渡动画类型(type),用来控制过渡动画的效果,有下面九种:
fade // 淡入淡出的效果(没有方向)
push // 新图层从一侧推入、旧图层从一侧推出来展示新外观效果
moveIn // 新图层从一侧推入来展示新外观效果、但旧图层没有从一侧推出
reveal // 旧图层从一侧推出来展示新外观效果、但新图层没有从一侧推入
oglFlip // 正面和背面的二维翻转效果
cube // 立方体翻转效果
pageCurl // 向上翻页效果
pageUnCurl // 向下翻页效果
suckEffect // 收缩效果
rippleEffect // 水滴波纹效果(没有方向)
过渡动画子类型(subType),用来控制过渡动画的方向(type里除了fade和rippleEffect没有方向之外,其它效果都有方向),有下面四种:
kCATransitionFromTop // 从底部开始过渡动画
kCATransitionFromLeft // 从左侧开始过渡动画
kCATransitionFromBottom // 从顶部开始过渡动画
kCATransitionFromRight // 从右侧开始过渡动画
过渡动画时间曲线(timingFunction),用来控制动画的节奏,有下面四种:
kCAMediaTimingFunctionEaseInEaseOut // 动画慢进,逐渐加快,逐渐减慢,慢出
kCAMediaTimingFunctionEaseIn // 动画慢进,逐渐加快
kCAMediaTimingFunctionEaseOut // 动画逐渐减慢,慢出
kCAMediaTimingFunctionLinear // 动画匀速
3、动画组
动画组没什么好说的,就像它的名字一样,它可以把多个属性动画、过渡动画给组合起来打包使用。比如上面关键帧动画的例子,我们创建了一个改变背景色的动画和一个平移动画,然后把它们分别添加了在CALayer身上,但是那俩动画明明就有很多一模一样的设置,重复写起来比较繁琐,这时我们就可以用动画组来简化一下代码。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *customLayer;
@property (nonatomic, strong) UIBezierPath *path;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customLayer = [[CALayer alloc] init];
self.customLayer.backgroundColor = [UIColor redColor].CGColor;
self.customLayer.frame = CGRectMake(100, 100, 100, 100);
[self.view.layer addSublayer:self.customLayer];
// 绘制一个三次贝塞尔曲线
self.path = [[UIBezierPath alloc] init];
[self.path moveToPoint:CGPointMake(150, 150)];
[self.path addCurveToPoint:CGPointMake(150, 450) controlPoint1:CGPointMake(250, 250) controlPoint2:CGPointMake(50, 350)];
// 通过shapelayer把贝塞尔曲线显示出来,
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = self.path.CGPath;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CAKeyframeAnimation *keyframeAnimation = [[CAKeyframeAnimation alloc] init];
keyframeAnimation.keyPath = @"backgroundColor";
keyframeAnimation.values = @[
(id)[UIColor redColor].CGColor,
(id)[UIColor orangeColor].CGColor,
(id)[UIColor yellowColor].CGColor,
(id)[UIColor greenColor].CGColor,
(id)[UIColor cyanColor].CGColor,
(id)[UIColor blueColor].CGColor,
(id)[UIColor purpleColor].CGColor,
];
CAKeyframeAnimation *keyframeAnimation1 = [[CAKeyframeAnimation alloc] init];
keyframeAnimation1.keyPath = @"position";
keyframeAnimation1.path = self.path.CGPath;
// 动画组
CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc] init];
animationGroup.animations = @[keyframeAnimation, keyframeAnimation1];
animationGroup.duration = 3;
animationGroup.removedOnCompletion = NO;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.delegate = self;
[self.customLayer addAnimation:animationGroup forKey:nil];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim {
NSLog(@"动画开始了");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(@"动画结束了");
}
@end
二、UIView Animation
实际开发中,我们用的最多的还是UIView而非CALayer,所以苹果又在UIKit框架里提供了UIView Animation,这就使得我们可以直接面向UIView来做动画了,而且API也比Core Animation简洁多了。
UIView Animation是作用在UIView层的,是对Core Animation的一一封装。
1、属性动画
所谓属性动画,就是作用于UIView属性的动画,UIView常见的可动画属性有:
- 最普通的属性:backgroundColor、frame、bounds、center
- 显隐性属性:alpha(注意hidden属性不支持)
- 仿射变换属性:transform
UIView和CALayer的仿射变换属性主要就是两个用途:一和动画配合,二和手势配合。
实际开发中我们最常见的就是对仿射变换属性做动画了,比如平移动画我们会用仿射变换的CGAffineTransformMakeTranslation来实现、而非frame或center,比如缩放动画我们会用仿射变换的CGAffineTransformMakeScale来实现、而非frame或bounds,旋转会用仿射变换的CGAffineTransformMakeRotation来实现。
1.1 基础动画
基础动画的API一共有四个:
- 最简单的
[UIView animateWithDuration: // 动画时长
animations:^{
// 要改变的属性
}];
- 带动画完成回调的
[UIView animateWithDuration: // 动画时长
animations:^{
// 要改变的属性
} completion:^(BOOL finished) {
// 动画完成的回调
}];
- 可设置延迟执行和自定义动画效果的
[UIView animateWithDuration: // 动画时长
delay: // 延迟多长时间后开始执行动画
options: // 可自定义动画效果,来替换掉系统默认的平滑效果
animations:^{
// 要改变的属性
} completion:^(BOOL finished) {
// 动画完成的回调
}];
- 弹性动画,是指在改变属性的时候,属性值会在目标值附近摆动,类似弹簧那种的弹性动画
[UIView animateWithDuration: // 动画时长
delay: // 延迟多长时间后开始执行动画
usingSpringWithDamping: // 弹性效果的阻尼:范围0~1,数值越小弹性效果越明显,震得越久才能停下来
initialSpringVelocity: // 弹性效果的初始速度:数值越大开始弹性动画时初始速度越快
options: // 可自定义动画效果,来替换掉系统默认的平滑效果
animations:^{
// 要改变的属性
} completion:^(BOOL finished) {
// 动画完成的回调
}];
options(UIViewAnimationOptions):
UIViewAnimationOptionTransitionNone // 动画效果:不使用动画
UIViewAnimationOptionTransitionCrossDissolve // 动画效果:淡入淡出
UIViewAnimationOptionTransitionFlipFromTop // 动画效果:正面和背面的二维翻转,从上向下翻转
UIViewAnimationOptionTransitionFlipFromLeft // 动画效果:正面和背面的二维翻转,从左向右翻转
UIViewAnimationOptionTransitionFlipFromBottom // 动画效果:正面和背面的二维翻转,从下向上翻转
UIViewAnimationOptionTransitionFlipFromRight // 动画效果:正面和背面的二维翻转,从右向左翻转
UIViewAnimationOptionTransitionCurlUp // 动画效果:翻页,从下往上翻页
UIViewAnimationOptionTransitionCurlDown // 动画效果:翻页,从上往下翻页
UIViewAnimationOptionCurveEaseInOut // 动画的缓冲曲线:动画慢进,逐渐加快,逐渐减慢,慢出(默认值)
UIViewAnimationOptionCurveEaseIn // 动画的缓冲曲线:动画慢进,逐渐加快
UIViewAnimationOptionCurveEaseOut // 动画的缓冲曲线:动画逐渐减慢,慢出
UIViewAnimationOptionCurveLinear // 动画的缓冲曲线:动画匀速
UIViewAnimationOptionRepeat // 重复执行动画
UIViewAnimationOptionAllowUserInteraction // 执行动画期间,开启view的用户交互
UIViewAnimationOptionAutoreverse // 执行动画回路
UIViewAnimationOptionLayoutSubviews // 执行动画时布局子控件
UIViewAnimationOptionAllowAnimatedContent // 执行动画时重绘视图
UIViewAnimationOptionBeginFromCurrentState // 从当前状态开始执行动画
UIViewAnimationOptionShowHideTransitionViews // 显示视图时显示或隐藏而不是移除或添加
UIViewAnimationOptionOverrideInheritedOptions // 不继承父动画设置
UIViewAnimationOptionOverrideInheritedCurve // 忽略嵌套动画的曲线设置
UIViewAnimationOptionOverrideInheritedDuration // 忽略嵌套动画的执行时间设置
对比上面Core Animation的例子,现在我们直接写个例子看看,给customView添加一个背景色变化(红色 -> 橙色)的动画和一个平移动画((100, 100) -> (200, 200))。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *customView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView = [[UIView alloc] init];
self.customView.backgroundColor = [UIColor redColor];
self.customView.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:self.customView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView animateWithDuration:3 animations:^{
self.customView.backgroundColor = [UIColor orangeColor];
}];
[UIView animateWithDuration:3 animations:^{
self.customView.transform = CGAffineTransformMakeTranslation(100, 100);
}];
}
@end
1.2 关键帧动画
需要注意的是UIView Animation的关键帧动画只能给属性设置若干个离散的值,而不能像Core Animation的关键帧动画那样还能给属性设置一条路径上无数个连续的值。关键帧动画的API一共有两个:
- 添加关键帧动画
[UIView animateKeyframesWithDuration: // 动画时长
delay: // 延迟多长时间后开始执行动画
options: // 可自定义动画效果,来替换掉系统默认的平滑效果
animations:^{
// 添加关键帧
} completion:^(BOOL finished) {
// 动画完成的回调
}];
options(UIViewKeyframeAnimationOptions):
UIViewKeyframeAnimationOptionCalculationModeLinear // 运算模式:连续,默认
UIViewKeyframeAnimationOptionCalculationModeDiscrete // 运算模式:离散
UIViewKeyframeAnimationOptionCalculationModePaced // 运算模式:均匀执行
UIViewKeyframeAnimationOptionCalculationModeCubic // 运算模式:平滑
UIViewKeyframeAnimationOptionCalculationModeCubicPaced // 运算模式:平滑均匀
UIViewKeyframeAnimationOptionRepeat = UIViewAnimationOptionRepeat // 重复执行动画
UIViewKeyframeAnimationOptionAllowUserInteraction = UIViewAnimationOptionAllowUserInteraction // 执行动画期间,开启view的用户交互
UIViewKeyframeAnimationOptionAutoreverse = UIViewAnimationOptionAutoreverse // 执行动画回路
UIViewKeyframeAnimationOptionLayoutSubviews = UIViewAnimationOptionLayoutSubviews // 执行动画时布局子控件
UIViewKeyframeAnimationOptionBeginFromCurrentState = UIViewAnimationOptionBeginFromCurrentState // 从当前状态开始执行动画
UIViewKeyframeAnimationOptionOverrideInheritedOptions = UIViewAnimationOptionOverrideInheritedOptions // 不继承父动画设置
UIViewKeyframeAnimationOptionOverrideInheritedDuration = UIViewAnimationOptionOverrideInheritedDuration // 忽略嵌套动画的执行时间设置
- 添加关键帧
[UIView addKeyframeWithRelativeStartTime: // 这一帧动画开始的时间点(占总时间的比例)
relativeDuration: // 这一帧动画的动画时长(占总时间的比例)
animations:^{
// 要改变的属性
}];
对比上面Core Animation的例子,现在我们直接写个例子看看,给customLayer添加一个背景色变化(红色 -> 橙色 -> 黄色 -> 绿色 -> 青色 -> 蓝色 -> 紫色)的动画。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *customView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView = [[UIView alloc] init];
self.customView.backgroundColor = [UIColor redColor];
self.customView.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:self.customView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView animateKeyframesWithDuration:3 delay:0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor redColor];
}];
[UIView addKeyframeWithRelativeStartTime:1/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor orangeColor];
}];
[UIView addKeyframeWithRelativeStartTime:2/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor yellowColor];
}];
[UIView addKeyframeWithRelativeStartTime:3/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor greenColor];
}];
[UIView addKeyframeWithRelativeStartTime:4/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor cyanColor];
}];
[UIView addKeyframeWithRelativeStartTime:5/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor blueColor];
}];
[UIView addKeyframeWithRelativeStartTime:6/7.0 relativeDuration:1/7.0 animations:^{
self.customView.backgroundColor = [UIColor purpleColor];
}];
} completion:^(BOOL finished) {
NSLog(@"动画结束了");
}];
}
@end
2、过渡动画
过渡动画的API一共有两个:
- 给单个UIView的不可动画属性添加过渡动画
[UIView transitionWithView: // 要做动画的视图
duration: // 动画时长
options: // 过渡动画效果
animations:^{
// 要改变的不可动画属性
} completion:^(BOOL finished) {
// 动画完成的回调
}];
- 给两个UIView切换时添加过渡动画
[UIView transitionFromView: // 旧视图
toView: // 新视图
duration: // 动画时长
options: // 动画过渡效果
completion:^(BOOL finished) {
// 动画完成的回调
}];
options(UIViewAnimationOptions):
UIViewAnimationOptionTransitionNone // 动画效果:不使用动画
UIViewAnimationOptionTransitionCrossDissolve // 动画效果:淡入淡出
UIViewAnimationOptionTransitionFlipFromTop // 动画效果:正面和背面的二维翻转,从上向下翻转
UIViewAnimationOptionTransitionFlipFromLeft // 动画效果:正面和背面的二维翻转,从左向右翻转
UIViewAnimationOptionTransitionFlipFromBottom // 动画效果:正面和背面的二维翻转,从下向上翻转
UIViewAnimationOptionTransitionFlipFromRight // 动画效果:正面和背面的二维翻转,从右向左翻转
UIViewAnimationOptionTransitionCurlUp // 动画效果:翻页,从下往上翻页
UIViewAnimationOptionTransitionCurlDown // 动画效果:翻页,从上往下翻页
UIViewAnimationOptionCurveEaseInOut // 动画的缓冲曲线:动画慢进,逐渐加快,逐渐减慢,慢出(默认值)
UIViewAnimationOptionCurveEaseIn // 动画的缓冲曲线:动画慢进,逐渐加快
UIViewAnimationOptionCurveEaseOut // 动画的缓冲曲线:动画逐渐减慢,慢出
UIViewAnimationOptionCurveLinear // 动画的缓冲曲线:动画匀速
UIViewAnimationOptionRepeat // 重复执行动画
UIViewAnimationOptionAllowUserInteraction // 执行动画期间,开启view的用户交互
UIViewAnimationOptionAutoreverse // 执行动画回路
UIViewAnimationOptionLayoutSubviews // 执行动画时布局子控件
UIViewAnimationOptionAllowAnimatedContent // 执行动画时重绘视图
UIViewAnimationOptionBeginFromCurrentState // 从当前状态开始执行动画
UIViewAnimationOptionShowHideTransitionViews // 显示视图时显示或隐藏而不是移除或添加
UIViewAnimationOptionOverrideInheritedOptions // 不继承父动画设置
UIViewAnimationOptionOverrideInheritedCurve // 忽略嵌套动画的曲线设置
UIViewAnimationOptionOverrideInheritedDuration // 忽略嵌套动画的执行时间设置
对比上面Core Animation的例子,现在我们直接写个例子看看,给label.layer上显示的文本和imageView.layer上显示的图片的变化添加过渡动画。
-----------ViewController.m-----------
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] init];
self.imageView.frame = CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.width);
// 首先展示修改前的图层外观
self.imageView.image = [UIImage imageNamed:@"乔丹.png"];
[self.view addSubview:self.imageView];
self.label = [[UILabel alloc] init];
self.label.frame = CGRectMake(0, self.view.bounds.size.width + 20, self.view.bounds.size.width, 30);
// 首先展示修改前的图层外观
self.label.text = @"乔丹";
self.label.textColor = [UIColor blackColor];
self.label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:self.label];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView transitionWithView:self.imageView duration:3 options:UIViewAnimationOptionTransitionCurlUp animations:^{
self.imageView.image = [UIImage imageNamed:@"科比.png"];
} completion:^(BOOL finished) {
NSLog(@"动画结束了");
}];
[UIView transitionWithView:self.label duration:3 options:UIViewAnimationOptionTransitionCurlUp animations:^{
self.label.text = @"科比";
} completion:^(BOOL finished) {
NSLog(@"动画结束了");
}];
}
@end
3、约束动画需注意
- 如果你用了frame,那做动画时就用frame做动画,如果你用了autolayout,那做动画时就用autolayout做动画,不要交叉着来,否则容易出问题
- 如果是用frame做动画,那在UIView Animation的block里直接修改frame就可以有动画了,但如果是用autolayout做动画,那在UIView Animation的block里直接修改约束却不会有动画,我们必须在修改约束后主动调用一下父控件的layoutIfNeeded方法来强制立即把约束转换成frame并刷新UI,这样才会有动画
-----------ViewController.m-----------
#import "ViewController.h"
#import "Masonry.h"
@interface ViewController ()
@property (strong, nonatomic) UIView *customView1;
@property (strong, nonatomic) UIView *customView2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView1 = [[UIView alloc] init];
self.customView1.backgroundColor = [UIColor redColor];
[self.view addSubview:self.customView1];
// 用frame
self.customView1.frame = CGRectMake(100, 100, 100, 100);
self.customView2 = [[UIView alloc] init];
self.customView2.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.customView2];
// 用autolayout
[self.customView2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(300);
make.left.equalTo(self.view).offset(100);
make.width.height.mas_equalTo(100);
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView animateWithDuration:3 animations:^{
// 用frame做动画
CGRect frame = self.customView1.frame;
frame.size.width = 200;
self.customView1.frame = frame;
}];
[UIView animateWithDuration:3 animations:^{
// 用autolayout做动画
[self.customView2 mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(200);
}];
// 调用一下父控件的layoutIfNeeded方法来强制立即把约束转换成frame并刷新UI
[self.view layoutIfNeeded];
}];
}
@end
三、Core Animation和UIView Animation怎么选择
基础动画:两者都可以,优先选择UIView Animation。
关键帧动画:两者都可以,优先选择UIView Animation,但是如果要做路径关键帧动画,则只能选择Core Animation。
过渡动画:两者都可以,优先选择UIView Animation,但是如果过渡效果找不到,则选择Core Animation,它的过渡效果更多。