iOS核心动画Core Animation分为几类:基础动画、关键帧动画、动画组、转场动画。关系大致如下图:
CAAnimation:核心动画的基础类,不能直接使用,负责动画运行时间、速度的控制,本身实现了CAMediaTiming协议。
CAAnimationGroup:动画组,动画组是一种组合模式设计,可以通过动画组来进行所有动画行为的统一控制,组中所有动画效果可以并发执行。
CAPropertyAnimation:属性动画的基类(通过属性进行动画设置,注意是可动画属性),不能直接使用。
CATransition:转场动画,主要通过滤镜进行动画效果设置。
CABasicAnimation:基础动画,通过属性修改进行动画参数控制,只有初始状态和结束状态。
CAKeyframeAnimation:关键帧动画,通过属性进行动画参数控制,可以有多个状态控制。
CASpringAnimation:弹簧动画,通过属性进行动画参数控制,可以有多个状态控制。但是9.0才有的属性。
基础动画、关键帧动画都属于属性动画,就是通过修改属性值产生动画效果,开发人员只需要设置初始值和结束值,中间的过程动画(又叫“补间动画”)由系统自动计算产生。和基础动画不同的是关键帧动画可以设置多个属性值,每两个属性中间的补间动画由系统自动完成。
基础动画
基础动画可以满足大部分的开发需求,UIView封装了一些方法提供使用,如果不使用封装方法,创建动画一般分为以下几步:
1、初始化动画,并指定动画属性
2、设置动画属性
3、给图层添加动画
下面移动动画作为实例,点击屏幕,物体移动到点击位置。
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (nonatomic, strong) CALayer *animationCALayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.animationCALayer = [[CALayer alloc] init];
self.animationCALayer.bounds = CGRectMake(0, 0, 40, 40);
self.animationCALayer.position = CGPointMake(50, 150);
self.animationCALayer.contents = (id)[UIImage imageNamed:@"vehicleResource.png"].CGImage;
[self.view.layer addSublayer:self.animationCALayer];
}
#pragma mark - 点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint location = [touch locationInView:self.view];
[self animationWhitPoint:location];
}
#pragma mark - 移动动画
- (void)animationWhitPoint:(CGPoint) location {
// 1、创建动画并指定动画属性
CABasicAnimation *myBasicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
// 2、设置动画属性
// myBasicAnimation.fromValue = [NSNumber numberWithInteger:50];
myBasicAnimation.toValue = [NSValue valueWithCGPoint:location];
// 设置动画其他属性 事件 重复次数 运行一次是否移除动画
myBasicAnimation.duration = 5;
// myBasicAnimation.repeatCount = 2;
// myBasicAnimation.removedOnCompletion = NO;
// 3、添加动画到图层,注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取
[self.animationCALayer addAnimation:myBasicAnimation forKey:@"KCBasicAnimation_Translation"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
基本效果已经达到,但是物体动画效果结束后又回到了原来的位置,然而使用UIView封装的方法就不会出现这种情况。应该怎么解决?
图层动画的本质就是将图层内部的内容转化为位图经硬件操作形成一种动画效果,其实图层本身并没有任何的变化。上面的动画中图层并没有因为动画效果而改变它的位置(对于缩放动画其大小也是不会改变的),所以动画完成之后图层还是在原来的显示位置没有任何变化,如果这个图层在一个UIView中你会发现在UIView移动过程中你要触发UIView的点击事件也只能点击原来的位置(即使它已经运动到了别的位置),因为它的位置从来没有变过。我们不妨在动画完成之后重新设置它的位置。
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController () <CAAnimationDelegate>
@property (nonatomic, strong) CALayer *animationCALayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.animationCALayer = [[CALayer alloc] init];
self.animationCALayer.bounds = CGRectMake(0, 0, 40, 40);
self.animationCALayer.position = CGPointMake(50, 150);
self.animationCALayer.contents = (id)[UIImage imageNamed:@"vehicleResource.png"].CGImage;
[self.view.layer addSublayer:self.animationCALayer];
}
#pragma mark - 点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint location = [touch locationInView:self.view];
[self animationWhitPoint:location];
}
#pragma mark - 移动动画
- (void)animationWhitPoint:(CGPoint) location {
// 1、创建动画并指定动画属性
CABasicAnimation *myBasicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
// 2、设置动画属性
// myBasicAnimation.fromValue = [NSNumber numberWithInteger:50];
myBasicAnimation.toValue = [NSValue valueWithCGPoint:location];
// 设置动画其他属性 事件 重复次数 运行一次是否移除动画
myBasicAnimation.duration = 3;
// myBasicAnimation.repeatCount = 2;
// myBasicAnimation.removedOnCompletion = NO;
// 3、添加动画到图层,注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取
// 设置代理
myBasicAnimation.delegate = self;
// 存储位置在动画结束后使用
[myBasicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
[self.animationCALayer addAnimation:myBasicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark - CAAnimationDelegate
/**
动画开始
*/
- (void)animationDidStart:(CAAnimation *)anim {
}
/**
动画结束
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
self.animationCALayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
给动画设置代理监听开始和结束,存储结束位置坐标,在动画结束时改变物体位置。但是问题是得到了解决,不过效果不佳,物体动画结束后又回到起始位置,然后瞬间移动到结束位置。是因为对于非根图层,设置图层的可动画属性(在动画结束后重新设置了position,而position是可动画属性)会产生动画效果。解决这个问题有两种办法:关闭图层隐式动画、设置动画图层为根图层。我们这里用关闭图层的隐式动画方法。只需要做如下修改:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
// 开启事务
[CATransaction begin];
// 禁用隐式动画
[CATransaction setDisableActions:YES];
self.animationCALayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
// 提交事务
[CATransaction commit];
}
还可以在移动的过程中添加旋转动画
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController () <CAAnimationDelegate>
@property (nonatomic, strong) CALayer *animationCALayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.animationCALayer = [[CALayer alloc] init];
self.animationCALayer.bounds = CGRectMake(0, 0, 40, 40);
self.animationCALayer.position = CGPointMake(50, 150);
self.animationCALayer.anchorPoint = CGPointMake(0.5, 0.5);
self.animationCALayer.contents = (id)[UIImage imageNamed:@"vehicleResource.png"].CGImage;
[self.view.layer addSublayer:self.animationCALayer];
}
#pragma mark - 点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint location = [touch locationInView:self.view];
[self animationWhitPoint:location];
[self rotationAnimation];
}
#pragma mark - 移动动画
- (void)animationWhitPoint:(CGPoint) location {
// 1、创建动画并指定动画属性
CABasicAnimation *myBasicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
// 2、设置动画属性
// myBasicAnimation.fromValue = [NSNumber numberWithInteger:50];
myBasicAnimation.toValue = [NSValue valueWithCGPoint:location];
// 设置动画其他属性 事件 重复次数 运行一次是否移除动画
myBasicAnimation.duration = 3;
// myBasicAnimation.repeatCount = 2;
// myBasicAnimation.removedOnCompletion = NO;
// 3、添加动画到图层,注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取
// 设置代理
myBasicAnimation.delegate = self;
// 存储位置在动画结束后使用
[myBasicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
[self.animationCALayer addAnimation:myBasicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark - 旋转动画
- (void) rotationAnimation {
// 1、创建动画并指定动画属性
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
// 2、设置动画属性初始值、结束值
// basicAnimation.fromValue = [NSNumber numberWithInt:M_PI_2];
basicAnimation.toValue = [NSNumber numberWithFloat:M_PI_2*3];
//设置其他动画属性
basicAnimation.duration = 3.0;
basicAnimation.autoreverses = false; // 旋转后再旋转到原来的位置
// 3、添加动画到图层,注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取
[self.animationCALayer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}
#pragma mark - CAAnimationDelegate
/**
动画开始
*/
- (void)animationDidStart:(CAAnimation *)anim {
}
/**
动画结束
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
// 开启事务
[CATransaction begin];
// 禁用隐式动画
[CATransaction setDisableActions:YES];
self.animationCALayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
// 提交事务
[CATransaction commit];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
弹性动画
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController () <CAAnimationDelegate>
@property (nonatomic, strong) CALayer *animationCALayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.animationCALayer = [[CALayer alloc] init];
self.animationCALayer.bounds = CGRectMake(0, 0, 40, 40);
self.animationCALayer.position = CGPointMake(50, 150);
self.animationCALayer.contents = (id)[UIImage imageNamed:@"image_3.jpg"].CGImage;
[self.view.layer addSublayer:self.animationCALayer];
}
#pragma mark - 点击事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint location = [touch locationInView:self.view];
[self animationWhitPoint:location];
}
#pragma mark - 移动动画
- (void)animationWhitPoint:(CGPoint) location {
// 1、创建动画并指定动画属性
CASpringAnimation *mySpringAnimation = [CASpringAnimation animationWithKeyPath:@"position"];
mySpringAnimation.delegate = self;
// 2、设置动画属性
mySpringAnimation.toValue = [NSValue valueWithCGPoint:location];
// damping:阻尼,范围0-1,阻尼越接近于0,弹性效果越明显
mySpringAnimation.damping = 0.6;
// initialVelocity:弹性复位的速度
mySpringAnimation.initialVelocity = 1.0;
// 设置动画其他属性 事件 重复次数 运行一次是否移除动画
mySpringAnimation.duration = 1;
[mySpringAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
// 3、添加动画到图层,注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取
[self.animationCALayer addAnimation:mySpringAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark - CAAnimationDelegate
/**
动画结束
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
// 开启事务
[CATransaction begin];
// 禁用隐式动画
[CATransaction setDisableActions:YES];
self.animationCALayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
// 提交事务
[CATransaction commit];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end