Core Animatio核心动画

本文深入探讨了Core Animation框架,包括其工作原理、基本动画、关键帧动画、组动画和转场动画等内容。通过丰富的代码示例介绍了如何利用Core Animation实现各种动画效果。

1. "简单介绍"

 

Core Animation  是一组非常强大的动画处理API,使用它能做出非常绚丽的动画效果,

可以在Mac OSiOS平台

核心动画执行过程是在后台操作的,不会阻塞主线程,注意的是核心动画是直接作用在CALayer上的,并非UIVIew

核心动画的本质:在后台移动图层中的内容,执行完毕后图层本身的位置并没有发生变化。

 

核心动画的基本动画的  forKey 设置和不设置有区别,如果设置就不能一直变化,没有设置就可以一直放大,移动或者缩小

CALayer 负责视图中显示内容和动画

UIVIew 负责监听和响应事件

 

//获取当前日历

NSCalendar * currentCalendar = [NSCalendar currentCalendar];

 

//获取当前时间

NSDate * currentDate = [NSDate date];

 

//用当前日历从当前时间获取秒

NSInteger  second = [currentCalendar component:NSCalendarUnitSecond fromDate:currentDate];

 

//方式二

//    NSDateComponents * com = [currentCalendar components:NSCalendarUnitSecond fromDate:currentDate];

//

//    NSInteger second = com.second;

 

 

手动创建的CALayer对象,都存在着隐式动画

 

CALayer 的隐式动画(可动画属性)

//开启动画事务

[CATransaction begin];

 

//关闭隐式动画

[CATransaction setDisableActions:YES];

 

//设置动画持续的时间

//    [CATransaction setAnimationDuration:3];

 

//3.修改redLayer的位置

self.redLayer.position = locP;

 

//提交

[CATransaction commit];

 

CALayertransform属性 他的transform就变成3D....什么什么了。它能做的就是连续放大或者缩小,及移动。还有移动,放大,缩小一次,

 

 

Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。

默认动画时长是0.25

核心动画的本质:在后台移动图层中的内容执行完毕后图层本身的位置并没有发生变化。

核心动画  基本动画

Core Animation    CABasicAnimation

平移 缩放 旋转  重要的属性

动画结束后不删除

basicAni.removedOnCompletion = NO;

 

保留当前状态

basicAni.fillMode = kCAFillModeForwards;

 

 

基本动画之关键帧动画  CAKeyframeAnimation

是可以根据路径来绘制的

还可以设置动画节奏 就是一些效果

//设置动画节奏

keyAnima.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];

 

核心动画之组动画 CAAnimationGroup

就是把基本动画和关键帧动画可以设置成他的子动画,可以一起执行

 

核心动画之转场动画  CATransition * trans = [CATransition animation];

可以做相册的上下张切换

里面可以设置类型

UIImageView 默认不支持用户交互,发现如果滑动不换图片,去开启用户交互就可以了

 

UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并会将所有内容绘制在自己的图层,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIVIew的显示

 

 

 

 

 

2. CALayer介绍

2.1. CALayer

1、在iOS,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本框、一个Label, 个图片等等,这些都是UIView

 

2、其实UIView之所以能显示在屏幕上,完全是应为它内部的一个图层layer

 

3、在创建UIView对象时,UIView内部会自动创建一个图层(CALayer),UIViewlayer属性

 

4、当UIView需要显示到屏幕上时,会调用drawRect方法进行绘图,并且会将所有内容绘制在自己的图层 ,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示

(绘图上下文是Layer Graphics Context)

 

5UIView本身不具备显示功能,是它内部的层才有显示功能

 

2.1 CALayer的常见属性

@property CGFloat borderWidth;

@property CGColorRef borderColor;

@property CGColorRef backgroundColor; //背景颜色

@property float opacity;//透明度

@property CGColorRef shadowColor; //阴影颜色

@property float shadowOpacity;

@property CGSize shadowOffset;

@property CGFloat shadowRadius;

@property(strong) id contents;

@property CGFloat cornerRadius;

@property CGRect bounds;

@property CGPoint position;

@property CGPoint anchorPoint;

@property CATransform3D transform;

@property(getter=isHidden) BOOL hidden;

@property(readonly) CALayer *superlayer;

@property(copy) NSArray *sublayers;

@property BOOL masksToBounds;

 

- 设置控制器 view 的背景颜色, 更好的观察

self.view.backgroundColor = [UIColor grayColor];

 

- 如何让"控件"变的更好看? 通过操作控件的 CALayer, 修改控件的外观。

- "阴影效果"

/** 参考:

 

 // "阴影"效果(shadowColorshadowOffsetshadowOpacity属性需要同时设置后才可以看到)

 // 设置"阴影"的颜色, 注意 UIKit 框架中的颜色不能直接设置给 CGColorRef

 self.demoView.layer.shadowColor = [UIColor yellowColor].CGColor;

 // 设置"阴影"的偏移

 self.demoView.layer.shadowOffset = CGSizeMake(5, 5);

 // 设置"阴影"的透明度(layer opacity 相当于 view alpha)

 self.demoView.layer.shadowOpacity = 1.0;

 

 

 // 设置"阴影"半径

 self.demoView.layer.shadowRadius = 10;

 

*/

 

 

- "圆角效果"

- 画图解释"圆角半径"的含义

- 注意: 对于正方形来说, 当圆角半径为边长的一半的时候, 就是一个圆形

- 可以用这种方式对控件截图

/** 参考:

 

// 设置"圆角"效果

self.demoView.layer.cornerRadius = 20;

 

*/

 

 

- 设置"边框效果"

/** 参考:

 

 // 设置"边框"效果

 self.demoView.layer.borderColor = [UIColor whiteColor].CGColor;

 self.demoView.layer.borderWidth = 2;

 

 */

 

 

- 通过 UIImageView 实现"图片裁剪"

/** 参考:

 

 - (void)viewDidLoad {

     [super viewDidLoad];

     

     // 设置控制器 view 的背景颜色, 更好的观察

     self.view.backgroundColor = [UIColor grayColor];

     

     [self demoImageView];

     

     //    // 设置"阴影"

     //    self.demoImageView.layer.shadowColor = [UIColor yellowColor].CGColor;

     //    self.demoImageView.layer.shadowRadius = 70;

     //    self.demoImageView.layer.shadowOffset = CGSizeMake(0, 0);

     //    self.demoImageView.layer.shadowOpacity = 1.0;

     

     self.demoImageView.layer.cornerRadius = 50;

     self.demoImageView.layer.borderWidth = 3;

     self.demoImageView.layer.borderColor = [UIColor blueColor].CGColor;

 

 

     // 注意: "阴影"效果" "裁剪"不能同时应用

     // CALayer masksToBounds 类似于 UIView clipsToBounds

     self.demoImageView.layer.masksToBounds = YES;

     

     

     

     // 保存图片

     UIGraphicsBeginImageContextWithOptions(self.demoImageView.bounds.size, NO, 0.0);

     CGContextRef ctx = UIGraphicsGetCurrentContext();

     

     [self.demoImageView.layer renderInContext:ctx];

     

     UIImage *imgIcon = UIGraphicsGetImageFromCurrentImageContext();

     UIGraphicsEndImageContext();

     

     UIImageWriteToSavedPhotosAlbum(imgIcon, nil, nil, nil);

 

 }

 

 */

 

- 2.2 "新建 CALayer", CALayer 中嵌套 CALayer(CALayer就像 UIView 一样, 也可以实现嵌套)

1> 在控制器的 self.view 中添加一个普通的 layer

/** 参考:

 - (void)viewDidLoad {

     [super viewDidLoad];

     // Do any additional setup after loading the view, typically from a nib.

     CALayer *layer = [[CALayer alloc] init];

     

     // ------------------- 设置位置大小 ---------------------

     // 方式一: 直接设置 frame

     // layer.frame = CGRectMake(50, 50, 200, 200);

     

     

     // 方式二:

     // 先设置大小

     layer.bounds = CGRectMake(0, 0, 100, 100);

     // 再设置位置(默认情况下 position 指的是 center)

     layer.position = CGPointMake(150, 150);

     // ------------------- 设置位置大小 ---------------------

     

     

     layer.backgroundColor = [UIColor redColor].CGColor;

     layer.opacity = 0.7;

     [self.view.layer addSublayer:layer];

 }

 

 */

 

2> 在控制器的 view 中添加一个能显示图片的 layer

- 思路:

* 向普通的自定义的 layer 中设置内容即可。设置 layer.contents属性。

* 查看 layer.contents属性的介绍

 

/** 参考:

 

 - (void)viewDidLoad {

 [super viewDidLoad];

 // Do any additional setup after loading the view, typically from a nib.

 CALayer *layer = [[CALayer alloc] init];

 

 // ------------------- 设置位置大小 ---------------------

 // 方式一: 直接设置 frame

 // layer.frame = CGRectMake(50, 50, 200, 200);

 

 

 // 方式二:

 // 先设置大小

 layer.bounds = CGRectMake(0, 0, 100, 100);

 // 再设置位置(默认情况下 position 指的是 center)

 layer.position = CGPointMake(150, 150);

 // ------------------- 设置位置大小 ---------------------

 

 

 layer.backgroundColor = [UIColor redColor].CGColor;

 layer.opacity = 1.0;

 

 UIImage *imgBg = [UIImage imageNamed:@"header_home"];

 // UIKit 中使用的是 Foundation 框架, Foundation 框架又使用的是 Core Foundation 框架

 // Core Foundation 框架都是 C 语言的, 一般凡是 Core Xxxx 的框架都是 C 语言的。

 // __bridge 类型 表达式, 的作用一般就是把 Core Foundation 中的数据类型转换成 Foundation 中的类型, 桥接的时候也会设置到一些所有权的转换等。

 layer.contents = (__bridge id)(imgBg.CGImage);

 [self.view.layer addSublayer:layer];

 

 

 }

 

 

 */

 

3> CALayer"可动画属性"

- 凡是文档中有"Animatable"字样的属性都是可动画属性

- 可动画属性就是说: 只要设置了属性(改变了属性), 会自动使用画的方式来执行。

 

// 通过position 修改 位置

- 案例: touchesBegan: 方法中, 获取当前触摸点的位置, 然后设置 layer position为这个触摸点。

* 效果: 设置完毕 position, 自动会以动画的方式来执行。(可动画属性)

/** 参考:

 

 @interface ViewController ()

 @property (nonatomic, weak) CALayer *myLayer;

 @end

 

 @implementation ViewController

 

 - (void)viewDidLoad {

 [super viewDidLoad];

 // Do any additional setup after loading the view, typically from a nib.

 CALayer *layer = [[CALayer alloc] init];

 self.myLayer = layer;

 

 // ------------------- 设置位置大小 ---------------------

 // 方式一: 直接设置 frame

 // layer.frame = CGRectMake(50, 50, 200, 200);

 

 

 // 方式二:

 // 先设置大小

 layer.bounds = CGRectMake(0, 0, 100, 100);

 // 再设置位置(默认情况下 position 指的是 center)

 layer.position = CGPointMake(150, 150);

 // ------------------- 设置位置大小 ---------------------

 

 

 layer.backgroundColor = [UIColor redColor].CGColor;

 layer.opacity = 1.0;

 

 UIImage *imgBg = [UIImage imageNamed:@"header_home"];

 // UIKit 中使用的是 Foundation 框架, Foundation 框架又使用的是 Core Foundation 框架

 // Core Foundation 框架都是 C 语言的, 一般凡是 Core Xxxx 的框架都是 C 语言的。

 // __bridge 类型 表达式, 的作用一般就是把 Core Foundation 中的数据类型转换成 Foundation 中的类型, 桥接的时候也会设置到一些所有权的转换等。

 layer.contents = (__bridge id)(imgBg.CGImage);

 [self.view.layer addSublayer:layer];

 

 

 }

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 UITouch *touch = touches.anyObject;

 CGPoint point = [touch locationInView:touch.view];

 

 self.myLayer.position = point;

 }

 

 @end

 

 */

总结: iOS 中的动画都是"属性动画", 只要修改 layer的动画属性, 即可实现动画。联想之前的[UIView animateWithDuration:]动画方法。

 

 

 

 

 

// 通过 transform 属性进行形变

- 案例: 通过 CALayer transform属性实现"旋转""缩放""平移"

1> 缩放:

/** 参考:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

     UITouch *touch = touches.anyObject;

     CGPoint point = [touch locationInView:touch.view];

     // 设置 layer 的位置(默认 position 表示中心点的位置)

     self.myLayer.position = point;

     

     //    // 缩放(生成一个0.6~ 1.0之间的随机数)

     //    CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5; //0.5;

     //    self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);

     

     // 旋转

     CGFloat rotate = arc4random_uniform(M_PI * 2);

     self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 5, 0);

 

 

 }

 */

 

2> 旋转

* 介绍 x, y, z

/** 参考:

 

  layer 的中心点到 给定的坐标点之间连一条线, 然后以这个线为中心轴, 开始旋转

 

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

     UITouch *touch = touches.anyObject;

     CGPoint point = [touch locationInView:touch.view];

     // 设置 layer 的位置(默认 position 表示中心点的位置)

     self.myLayer.position = point;

     

     // 缩放(生成一个0.6~ 1.0之间的随机数)

     CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5; //0.5;

     self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);

     

     // 旋转

     CGFloat rotate = arc4random_uniform(M_PI * 2);

     self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 0, 10);

 }

 

 */

 

//  问题: touchesBegan 方法中先后指定了 transform, 这样后指定的 transform 会覆盖前面的 transform, 所以现在的效果是只旋转,不缩放了。

// 解决: 通过 kvc 解决:

 

/** 参考解决办法:

 

 // 缩放(生成一个0.6~ 1.0之间的随机数)

 CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5;

 //self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);

 [self.myLayer setValue:@(scale) forKeyPath:@"transform.scale"];

 

 // 旋转

 CGFloat rotate = arc4random_uniform(M_PI * 2);

 //self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 0, 10);

 [self.myLayer setValue:@(rotate) forKeyPath:@"transform.rotation"];

 

 */

 

/**

 

 struct CATransform3D

 {

     CGFloat m11, m12, m13, m14;

     CGFloat m21, m22, m23, m24;

     CGFloat m31, m32, m33, m34;

     CGFloat m41, m42, m43, m44;

 };

 

 

 struct CGAffineTransform {

     CGFloat a, b, c, d;

     CGFloat tx, ty;

 };

 

 */

 

 

// 圆角,透明度,边框

/** 参考:

 

 // 圆角

 self.myLayer.cornerRadius = 20;

 self.myLayer.masksToBounds = YES;

 

 // 透明度

 self.myLayer.opacity = 0.5;

 

 

 // 设置边框

 self.myLayer.borderColor = [UIColor redColor].CGColor;

 self.myLayer.borderWidth = 5;

 

 

 */

 

Quartz Core框架不需要导入, 已经默认导入了

 

 

 

- CALayer position属性和 anchorPoint属性介绍

* anchorPoint是一个 CGPoint类型, 每个值都是一个0~1之间的值

 

 

-----------------------------------------------------------------

- 动画属性(隐式动画)需要注意点:

* 每一个UIView内部都默认关联着一个CALayer, 我们可称这个LayerRoot Layer(根层)

* 所有的非Root Layer, 也就是手动创建的CALayer对象, 都存在着隐式动画。 root layer 是没有隐式动画的

* 关闭隐式动画

/** 参考;

 

 可以通过动画事务(CATransaction)关闭默认的隐式动画效果

 [CATransaction begin];

 [CATransaction setDisableActions:YES];

 self.myview.layer.position = CGPointMake(10, 10);

 [CATransaction commit];

 

 

 //-------------------------------------------

 - (void)viewDidLoad {

 [super viewDidLoad];

 // Do any additional setup after loading the view, typically from a nib.

 CALayer *layer = [[CALayer alloc] init];

 layer.backgroundColor = [UIColor blueColor].CGColor;

 layer.bounds = CGRectMake(0, 0, 100, 100);

 layer.position = CGPointMake(200, 200);

 [self.view.layer addSublayer:layer];

 self.blueLayer = layer;

 }

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 UITouch *touch = touches.anyObject;

 CGPoint point = [touch locationInView:touch.view];

 

 [CATransaction begin];

 [CATransaction setDisableActions:YES];

 self.blueLayer.position = point;

 

 [CATransaction commit];

 }

 //-------------------------------------------

 

 */

 

- 案例练习:(时钟动画)

- 使用 CALayer"定位点(锚点)"实现一个时钟动画(通过 UIView 来实现,目的是为了说明控件的 center 其实就是 CALayer position, 通过修改了 position其实就修改了 center 的含义了)

* 注意: 在使用 Quartz2D 绘图的时候, 对绘图上下文的旋转是对坐标系的旋转, 通过 UI 控件的 transform 对控件做旋转是按照 Center 来旋转的。

* 注意: 控件的 center 属性, 其实就是对应的 CALayer postion。所以控件的 center并不是永远表示控件的中心点。

 

* 解决不准确的问题:

通过 NSTimer 实现的动画可能造成卡顿、不连贯的情况(NSTimer 不准确)

• CADisplayLink 表示"显示连接", 与显示器的刷新频率相关。

显示器每秒钟刷新60次(60HZ)(电视新闻上拍的电脑显示器就不清楚,原因刷新频率不一样)

通过 CADisplayLink 来解决

/** 参考代码:

 - (void)viewDidLoad {

     [super viewDidLoad];

     // Do any additional setup after loading the view, typically from a nib.

     

     //1.Layer创建表盘

     CALayer * clockLayer = [[CALayer alloc] init];

     

     [self.view.layer addSublayer:clockLayer];

     

     clockLayer.bounds = CGRectMake(0, 0, 150, 150);

     clockLayer.position = self.view.center;

     clockLayer.contents = (__bridge id)([UIImage imageNamed:@"clock"].CGImage);

     

     //2.创建秒针 其实就是一个很细的View

     

     UIView * lineView = [[UIView alloc] init];

     lineView.backgroundColor = [UIColor redColor];

     lineView.bounds = CGRectMake(0, 0, 1, 55);

     lineView.layer.anchorPoint = CGPointMake(0.5, 1);

     lineView.layer.position = clockLayer.position;

     [self.view addSubview:lineView];

     self.lineView = lineView;

     //让秒针动起来 (定时器)

 //    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(fire) userInfo:nil repeats:YES];

 //    [timer fire];

     //也可以先调用以下fire的方法

 //    [self fire];

     //解决与系统时钟秒针的稍微差别  这时候要用CADisplayLink这个对象 (屏幕刷新率)

     CADisplayLink * link = [CADisplayLink displayLinkWithTarget:self selector:@selector(fire2)];

     [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

     

 }

 - (void) fire2

 {

     //获取当前的日历

     NSCalendar * calender = [NSCalendar currentCalendar];

     

     //获取当前时间

     NSDate * date = [NSDate date];

     

     //calender中获取秒的部分

     NSDateComponents * components = [calender components:NSCalendarUnitSecond fromDate:date];

     //获取秒

     NSInteger senond = components.second;

     

     //计算一秒是多少弧度

     //一圈的弧度是 M_PI * 2   一圈是60 可得:

     CGFloat angle = M_PI * 2 /60;

     

     //获取当前需要旋转的角度

     CGFloat currentAngle = senond * angle;

     //让秒针旋转

     self.lineView.transform = CGAffineTransformMakeRotation(currentAngle);

 }

 - (void) fire

 {

    

      这么做的话与系统的时钟秒针不一致

      解决:首先获取当前时间的秒数  让这个秒数*angle  得出首先让秒针旋转到的位置

      

    //获取当前的日历

    NSCalendar * calender = [NSCalendar currentCalendar];

    

    //获取当前时间

    NSDate * date = [NSDate date];

    

    //calender中获取秒的部分

    NSDateComponents * components = [calender components:NSCalendarUnitSecond fromDate:date];

    //获取秒

    NSInteger senond = components.second;

    //计算一秒是多少弧度

    //一圈的弧度是 M_PI * 2   一圈是60 可得:

    CGFloat angle = M_PI * 2 /60;

    //获取当前需要旋转的角度

    CGFloat currentAngle = senond * angle;

    //让秒针旋转

    self.lineView.transform = CGAffineTransformMakeRotation(currentAngle);

}

*/

 

 

 

 

 

2. 核心动画

- 属性动画

1> 基本动画(只有两帧的动画)

2> 关键帧动画

 

- 组动画

 

- 转场动画(场景转换)

 

 

2.1 基本动画: 位移

案例: 触摸屏幕, 设置指定的 view layer position

/** 参考:

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 // 1. 创建动画对象

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];

 // 默认动画时间是0.25

 animation.duration = 2.0;

 

 // 2. 设置动画属性值

 animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];

 animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];

 

 // 3. 将动画添加到对应的layer

 [self.blueView.layer addAnimation:animation forKey:@"animation1"];

 }

 

 

 - (void)viewDidLoad {

 [super viewDidLoad];

 

 UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(50, 150, 100, 100)];

 blueView.backgroundColor = [UIColor blueColor];

 [self.view addSubview:blueView];

 self.blueView = blueView;

 }

 

 - 问题:

 1> 动画太快, 默认时间是0.25, 通过 duration 属性修改

 2> 动画执行完毕以后回到了起始的位置。

 • 原因:

 • 核心动画的本质是在后台移动图层中的内容, 控件本身的 frame 没有发生改变。

 • 所以看到动画执行完毕后, 又回到了原来的位置

 • 通过设置动画代理来观察动画执行完毕后控件的 frame , layer Frame , layer position , 都是没有变化的

 

 • 解决:

 解决1:当动画执行完毕以后, 手动设置控件的位置。在动画的代理方法(动画结束的时候设置控件的 center

 //self.blueView.center = CGPointMake(300, 50);

 注意: 不指定 fromValue 的情况下, 如果直接在添加完毕动画后, 设置控件的 center = 最终的终点有问题!!!所以不要在添加完动画以后直接设置 center 为最终的终点, 而要放到代理方法中。

 

 解决2

 动画执行完毕后不要删除动画对象

 设置 fillMode

 // 当动画执行完毕后不要删除动画对象

 animation.removedOnCompletion = NO;

 animation.fillMode = kCAFillModeForwards;

 缺点: 无法与用户交互, 因为 frame 就没变

 

 3> 介绍隐式代理

 4> 不指定 fromValue 就是从当前位置开始动画

 5> 注意:

 - 如果每次执行完毕动画都设置控件的 frame 为最终的 frame, 那么在连续多次点击的时候, 每次控件都要从上次结束出开始动画, 效果不好

 - 如果通过"动画执行完毕后不要删除动画对象", 动画比较流畅, 但是控件的 frame 始终没有变化, 将来可能会影响与用户交互

 

 */

 

 

 

/** 参考:

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 // 1. 创建动画对象

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];

 // 默认动画时间是0.25

 animation.duration = 2.0;

 // 设置动画代理

 animation.delegate = self;

 

 // 当动画执行完毕后不要删除动画对象

 animation.removedOnCompletion = NO;

 animation.fillMode = kCAFillModeForwards;

 

 // 2. 设置动画属性值

 //animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];

 animation.fromValue = [NSValue valueWithCGPoint:self.blueView.center];

 animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];

 

 // 3. 将动画添加到对应的layer

 [self.blueView.layer addAnimation:animation forKey:@"animation1"];

 

 // 动画执行完毕后设置控件的 center

 //NSLog(@"★");

 //self.blueView.center = CGPointMake(300, 50);

 

 

 }

 

 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

 {

 NSLog(@"%@----", NSStringFromCGRect(self.blueView.frame));

 

 NSLog(@"========%@", NSStringFromCGRect(self.blueView.layer.frame));

 

 NSLog(@"========%@", NSStringFromCGPoint(self.blueView.layer.position));

 //self.blueView.center = CGPointMake(300, 50);

 }

 

 

 

 //-------------------- 参考-----------------

 // 1. 创建动画对象

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];

 // 默认动画时间是0.25

 animation.duration = 1.0;

 // 设置动画代理

 animation.delegate = self;

 

 // 当动画执行完毕后不要删除动画对象

 animation.removedOnCompletion = NO;

 animation.fillMode = kCAFillModeForwards;

 

 // 2. 设置动画属性值

 //animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];

 // animation.fromValue = [NSValue valueWithCGPoint:self.blueView.center];

 //animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];

 animation.toValue = [NSValue valueWithCGPoint:location];

 

 // 3. 将动画添加到对应的layer

 

 // 这样每次会添加一个新的动画

 //self.blueView.layer addAnimation:animation forKey:nil] ;

 // 注意: 如果下面的 key 指定了一个写死的 key,@"animation100",这样不会每次都添加一个新的动画了。

 [self.blueView.layer addAnimation:animation forKey:@"animation100"];

 

 // 动画执行完毕后设置控件的 center

 //NSLog(@"★");

 //self.blueView.center = CGPointMake(300, 50);

 

 

 */

 

 

2.2 基本动画: 缩放动画

/** 参考:

 

 // 缩放动画

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 

 // 创建动画对象

 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];

 // 设置动画属性

 anim.fromValue = @(1.0f);

 anim.toValue = @(0.7f);

 // 设置重复次数

 anim.repeatCount = 10;

 

 // 将动画对象添加到 layer

 [self.blueView.layer addAnimation:anim forKey:nil];

 }

 

 */

 

 

 

2.3 基本动画: 旋转动画

/** 参考

 // 旋转动画

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 

 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

 

 anim.duration = 3;

 anim.repeatCount = CGFLOAT_MAX;

 // 不要每次都设置起始度数

 //anim.fromValue = @(0);

 anim.toValue = @(M_PI * 2);

 

 [self.blueView.layer addAnimation:anim forKey:nil];

 }

 */

* 注意: 如果 [self.blueView.layer addAnimation:anim forKey:nil];没有指定 forKey, 那么每次都会添加一个新动画, 会越来越快

* 解决:

/** 参考:

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 // 判断如果已经有动画对象了, 就不再添加了

 if ([self.blueView.layer animationForKey:@"anim1"] != nil) {

 return;

 }

 

 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

 

 anim.duration = 3;

 anim.repeatCount = CGFLOAT_MAX;

 // 不要每次都设置起始度数

 //anim.fromValue = @(0);

 anim.toValue = @(M_PI * 2);

 

 [self.blueView.layer addAnimation:anim forKey:@"anim1"];

 }

 

 */

* 注意: 如果当动画正在执行的时候, 将程序退出到后台, 那么当程序再次进入前台的时候就不执行了。

* 原因: 因为再次进入前台后动画已经被删除了。

* 解决1: anim.removedOnCompletion = NO;

* 问题: 当双击 home 键的时候, 动画不会暂停。

* 解决:

/** 参考:

 

 // 暂停

 - (void)applicationWillResignActive:(UIApplication *)application {

 ViewController *vc =  (ViewController *)self.window.rootViewController;

 [vc pause];

 }

 

 // 恢复

 - (void)applicationDidBecomeActive:(UIApplication *)application {

 ViewController *vc =  (ViewController *)self.window.rootViewController;

 [vc resume];

 }

 */

 

 

2.4 关键帧动画

- 设置 values 属性

/** 参考:

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];

 anim.duration = 3;

 anim.removedOnCompletion = NO;

 CGPoint p1 = CGPointMake(10, 10);

 CGPoint p2 = CGPointMake(10, 110);

 CGPoint p3 = CGPointMake(110, 110);

 CGPoint p4 = CGPointMake(110, 10);

 CGPoint p5 = CGPointMake(10, 10);

 anim.values = @[[NSValue valueWithCGPoint:p1], [NSValue valueWithCGPoint:p2], [NSValue valueWithCGPoint:p3], [NSValue valueWithCGPoint:p4],[NSValue valueWithCGPoint:p5]];

 

 [self.blueView.layer addAnimation:anim forKey:nil];

 

 

 }

 

 */

 

- 设置 path 属性

/** 参考:

 

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];

 anim.duration = 3;

 anim.removedOnCompletion = NO;

 anim.fillMode = kCAFillModeForwards;

 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 150, 150)];

 anim.path = path.CGPath;

 

 [self.blueView.layer addAnimation:anim forKey:nil];

 

 

 }

 

 */

 

 

- 模拟"app 抖动"

* 思路: 通过设置左右旋转实现

/** 参考:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 if ([self.blueView.layer animationForKey:@"shake"]) {

 return;

 }

 

 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];

 anim.values = @[@(-M_PI / 36), @(M_PI / 36), @(-M_PI / 36)];

 anim.duration = 0.15;

 anim.repeatCount = CGFLOAT_MAX;

 [self.blueView.layer addAnimation:anim forKey:@"shake"];

 

 }

 

 */

 

 

 

- 组动画

/** 参考:

 

 // 动画组, 组动画

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

 {

 UITouch *touch = touches.anyObject;

 CGPoint location = [touch locationInView:touch.view];

 

 

 CAAnimationGroup *groupAnim = [[CAAnimationGroup alloc] init];

 

 // 位移

 CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:@"position"];

 anim1.toValue = [NSValue valueWithCGPoint:location];

 

 // 缩放

 CABasicAnimation *anim2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];

 anim2.toValue = @(0.3);

 

 

 // 旋转

 CABasicAnimation *anim3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

 anim3.toValue = @(M_PI * 8);

 

 // 关键帧动画

 CAKeyframeAnimation *anim4 = [CAKeyframeAnimation animationWithKeyPath:@"position"];

 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(180, 150, 150, 150)];

 anim4.path = path.CGPath;

 

 groupAnim.animations = @[anim1, anim2, anim3, anim4];

 groupAnim.duration = 1.0;

 groupAnim.repeatCount = CGFLOAT_MAX;

 

 [self.blueView.layer addAnimation:groupAnim forKey:nil];

 

 

 

 }

 

 */

 

 

- 转场动画

案例: 通过在 view 中放一个图片框, "控件库"中拖拽两个"轻扫手势"(拖到哪个控件上, 就表示应用到了哪个控件上, 打开看 storyboard 中的 xml 文件),然后为"轻扫手势"拖线连接处理程序, 在处理程序中实现"转场动画"

/** 参考:

 

 - (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {

 

 // 创建一个转场动画对象

 CATransition *anim = [[CATransition alloc] init];

 // 设置过度类型

 anim.type = @"cube";

 

 if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {

 anim.subtype = kCATransitionFromRight;

 } else {

 anim.subtype = kCATransitionFromLeft;

 }

 

 [self.imgViewIcon.layer addAnimation:anim forKey:nil];

 

 }

 

 

 

 //-------------------------------------------------------

 @interface ViewController ()

 @property (weak, nonatomic) IBOutlet UIImageView *imgViewIcon;

 @property (nonatomic, assign) int index;

 @end

 

 @implementation ViewController

 

 - (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {

 

 // 创建一个转场动画对象

 CATransition *anim = [[CATransition alloc] init];

 // 设置过度类型

 anim.type = @"cube";

 

 if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {

 anim.subtype = kCATransitionFromRight;

 self.index--;

 } else {

 anim.subtype = kCATransitionFromLeft;

 self.index++;

 }

 if (self.index < 0) {

 self.index = 4;

 }

 if (self.index > 4) {

 self.index = 0;

 }

 

 NSString *imgName = [NSString stringWithFormat:@"%d", (self.index + 1)];

 self.imgViewIcon.image = [UIImage imageNamed:imgName];

 

 [self.imgViewIcon.layer addAnimation:anim forKey:nil];

 

 }

 

 

 

 

 //-------------- 通过 uiview 实现转场动画-----------

 - (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {

 

 

 UIViewAnimationOptions option;

 if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {

 option = UIViewAnimationOptionTransitionCurlUp;

 self.index--;

 } else {

 option = UIViewAnimationOptionTransitionCurlDown;

 self.index++;

 }

 if (self.index < 0) {

 self.index = 4;

 }

 if (self.index > 4) {

 self.index = 0;

 }

 

 NSString *imgName = [NSString stringWithFormat:@"%d", (self.index + 1)];

 self.imgViewIcon.image = [UIImage imageNamed:imgName];

 

 [UIView transitionWithView:self.view duration:1.0 options:option animations:^{

 // 在转场动画的过程中还可以执行其他的动画

 } completion:^(BOOL finished) {

 // 动画执行完毕后要做的事情

 }];

 

 

 }

 

 */

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/lufeidexin/blog/672538

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值