iOS之UI高级---CALayer和CAAnimation的混合使用

来源:互联网 发布:js 事件绑定区别 编辑:程序博客网 时间:2024/05/19 19:12
开篇之前,我的内心是复杂的,因为CALayer和CAAnimation这两个类包含的内容实在太多了。仅仅靠一篇文章不但无法很好的了解,甚至很久以后想要回来查阅时都不好开头。所以,我决定以关键词的方式来说明,当以后运用到相关知识的时候再根据关键词进行查阅。

PS:以下是两篇很好的文章,通读一遍也就没什么不能理解的了。我上传到我的github上。并且附带了我实践时使用的工程。
传送门:
https://github.com/chenyongMAC/CALayer-CAAnimation

一:CALayer和CAAnimation的介绍

1.CALayer:
①渲染结构
1)CALayer和UIView的区别:
CALayer:图层不会直接渲染到屏幕上
UIView:是iOS系统中界面元素的基础,所有元素继承于它,它本身由CoreAnimation来实现。它本身更像一个CALayer的管理器,一个UIView上可以有N个CALayer

②事务管理:
1)rootlayer和非root layer:
如果一个Layer对象存在对应着的View,则称这个Layer是一个Root Layer,非Root Layer一般都是通过CALayer或其子类直接创建的.下面的subLayer就是一个典型的非Root Layer,它没有对应的View对象关联着.

subLayer = [[CALayer alloc] init];    subLayer.frame = CGRectMake(0, 0, 300, 300);    subLayer.backgroundColor = [[UIColor redColor] CGColor];    [self.view.layer addSublayer:subLayer];

2)显示动画和隐式动画
所有的非Root Layer在设置Animatable Properties的时候都存在着隐式动画,默认的duration是0.25秒.

3)事务管理方式:
任何Layer的animatable属性的设置都应该属于某个CA事务(CATransaction),事务的作用是为了保证多个animatable属性的变化同时进行,不管是同一个layer还是不同的layer之间的.
CATransaction也分两类,显式的和隐式的。当在某次RunLoop中设置一个animatable属性的时候:
a)如果发现当前没有事务,则会自动创建一个CA事务,在线程的下个RunLoop开始时自动commit这个事务
b)如果在没有RunLoop的地方设置layer的animatable属性,则必须使用显式的事务.显示事务如下:

[CATransaction begin];...  [CATransaction commit];

③时间系统:CALayer实现了CAMediaTiming协议
关键字:(一下关键字,在我提供的文章里有非常详细的介绍,希望深层次理解的同学可以自行下载。下文代码示例中,我只提供了简单的使用方式)
beginTime
timeOffset
speed
fillmode
duration

二:代码示例:

①动画的暂停和继续:

#import "ViewController.h"@interface ViewController () {    UIImageView *_imageView;    UIButton *_pauseBtn;    UIButton *_resumeBtn;}@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];    _imageView.image = [UIImage imageNamed:@"马里奥"];    [self.view addSubview:_imageView];    //1.添加动画    CABasicAnimation *basic = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];    basic.fromValue = @0;    basic.toValue = @(2*M_PI);    basic.duration = 2;    basic.repeatCount = 10;    [_imageView.layer addAnimation:basic forKey:@"rotationAnimation"];    _pauseBtn = [UIButton buttonWithType:UIButtonTypeSystem];    _pauseBtn.frame = CGRectMake(100, 300, 50, 30);    [_pauseBtn setTitle:@"暂停" forState:UIControlStateNormal];    [_pauseBtn addTarget:self action:@selector(pauseBtnAction:) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:_pauseBtn];    _resumeBtn = [UIButton buttonWithType:UIButtonTypeSystem];    _resumeBtn.frame = CGRectMake(200, 300, 50, 30);    [_resumeBtn setTitle:@"继续" forState:UIControlStateNormal];    [_resumeBtn addTarget:self action:@selector(resumeBtnAction:) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:_resumeBtn];}-(void) pauseBtnAction:(UIButton *)sender {    //1.获取图层    CALayer *layer = _imageView.layer;    //2.获取图层暂停时候的“当前时间”    CFTimeInterval pauseTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];    //3.速度为0,即停止动画    layer.speed = 0;    //4.记录图层相对于开始动画的时间偏移    layer.timeOffset = pauseTime;}-(void) resumeBtnAction:(UIButton *) sender {    //1.获取图层    CALayer *layer = _imageView.layer;    //2.速度为1,即开始正常速度的动画    layer.speed = 1;    //3.获取动画时间偏移    CFTimeInterval pauseTime = layer.timeOffset;    //4.清空时间数据(为了重新获取图层的时间偏移)    layer.timeOffset = 0;    layer.beginTime = 0;    CFTimeInterval resumeTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];    CFTimeInterval time = resumeTime - pauseTime;    //5.通过时间偏移量,保证图层开始动画的时间和停止动画时候的时间相同(即图片动画效果连续)    layer.beginTime = time;}

讲解:

首先,我们先了解一些关键字。

①CAMediaTiming
CALayer和CAAnimation都实现了CAMediaTiming协议。这个协议实现了一个有层级关系的事件系统。
在CALayer中,有一个绝对时间的概念,通过CACurrentMediaTime()获得。它是mach_absolute_time()转换成秒后的值,和系统的重启时间有关

②beginTime:
无论是图层还是动画,都有一个时间线Timeline的概念,他们的beginTime是相对于父级对象的开始时间。所以对于图层而言,虽然创建有先后,但是他们的时间线都是一致的(只要不主动去修改某个图层的beginTime),所以我们可以想象成所有的图层默认都是从系统重启后开始了他们的时间线的计时。

下面来说明为什么将上面代码中的layer.beginTime = resumeTime - pauseTime;

这里写图片描述

①pauseTime和resumeTime都是相对于timeline开始的,所以是绝对时间
②虽然动画被我们暂停了(speed=0),但是它还是跟着timeline走的。如果恢复动画(speed=1),那他会根据当时的timeline跳转到对应的时间继续动画(这样会省略掉停止动画期间的动画内容,显然这不是我们希望的)。
③所以我们设置layer的beginTime=(暂停动画的时间)。由于动画的父对象是layer,所以它的开始时间为CACurrentMediaTime()+beginTime。这样,动画开始的时间就跟上了layer的当前时间,确保了动画的同步。

0 0
原创粉丝点击