iOS Core Animation (一) 基础动画

iOS核心动画Core Animation分为几类:基础动画、关键帧动画、动画组、转场动画。关系大致如下图:


关系图.png

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
动态图1.gif

基本效果已经达到,但是物体动画效果结束后又回到了原来的位置,然而使用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

动态图2.gif

 给动画设置代理监听开始和结束,存储结束位置坐标,在动画结束时改变物体位置。但是问题是得到了解决,不过效果不佳,物体动画结束后又回到起始位置,然后瞬间移动到结束位置。是因为对于非根图层,设置图层的可动画属性(在动画结束后重新设置了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

动态图3.gif

 弹性动画

#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
动态图4.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容