iOS开发之动画中的时间(概况)
一、引言
在iOS开发中使用动画时,可以通过设置动画的duration、speed、begintime、offset属性,来设置动画的时长、速度、起始时间及起始偏移。
用一个简单的例子来说明各个参数的的作用。动画很简单,一个红色的方块从左移到右边。动画的持续时间是1s,没有重复,效果如下。

CFTimeInterval currentTime = CACurrentMediaTime();
CFTimeInterval currentTimeInLayer = [self.testLayer convertTime:currentTime fromLayer:nil];
CFTimeInterval addTime = currentTimeInLayer;
anim.beginTime = 0.3 + addTime;
[anim setTimeOffset:0.5];
[anim setSpeed:2];
做修改以后,效果如下:

与上面相比,三处不同
- 动画的速度是原来的两倍。
- 点击开始动画的按钮,到开始动画,有一个延迟。
- 动画起始时,滑块的位置为中央,而不是在左边。
我们已经看到了这些属性的效果。翻阅文档,发现begintime、speed等属性是CAMediaTiming这协议的属性,并且CALayer、CAAnimation都遵守了CAMediaTiming协议。
那么CAMediaTiming协议是什么呢?有什么作用呢?
二、层级时间结构
根据文档,CMediaTiming协议构建了一个层级的时间系统,并用这个层级的时间系统来协调各个layer、animation的时间。
这个协议被CAAnimation及CALayer遵守,每一个遵守协议的的object对应一个time space。根据object之间的关系,不同的time space有层级关系。比如Layer A有一个subLayer B,那么Layer A对应的time space就是layer B对应的time space的parent time space。每一个time space中时间的数值都是根据parent time space的数值,以及begintime、speed等属性,根据一定的规则来计算的。
为了便于理解层级时间系统,先看下layer在屏幕上的显示位置是如何确定的,然后做一个类比。

layer层级如上。要确定sublayer1在屏幕上的显示位置,一共分三步。
- 确定window layer在屏幕位置position1
- 根据position1及view layer的position属性,确定view layer在屏幕中的位置position2
- 根据position2及sublayer1的position属性,确定sublayer1在屏幕中的位置position3
与此类似,要确定sub1ayer1中的time,也要分三步。
- 确定window layer中的time1
- 根据time1及view layer的
begintime、offset等属性计算出view layer中的time2 - 根据time2及sublayer1的
begintime、offset等属性计算出sublayer中的time3
和确定layer的位置相比,确定时间有一些复杂,主要提现在下面两点
- 层级时间系统的构成复杂。
layer tree的每一级都是CALayer,而只要遵守CAMediaTiming协议,就可以作为层级时间系统的一部分。比如CALayer、CAAnimation(及其子类CAAnimationGroup)都可以作为层级时间系统的一部分。 - 不同层级之间时间转换规则复杂
计算当前layer的位置时,只需要知道父layer的位置,以及当前layer的position属性。计算当前层级时间时,不仅需要知道上一个层级的时间,还需要知道当前层级的begintime、offset、speed等属性。转换的规则也比较复杂,要经历两次转换。从parent time到active local time,再到basic local time。
三、active local time
这次转换是为了处理当前层级的object在父层级的的时间线上的位置,以及当前层级和父层级之间时间流逝速度的关系。
和这次转换相关的属性有beginTime、speed以及timeOffset
begin time
子层级相对于父层级的起始时间。也就是父层级的时间经过多久,子层级才开始计算时间。
比如子层级A被加入层级时间系统时,它父层级B的时间是5s,子层级A的begintime是6s,那么当它父层级的时间变为6s时,子层级才开始计算时间。speed
子层级相对于父层级的时间流逝速度。如果speed是2,那么当父层级的时间增加了10s时,子层级的时间增加了20s(10s的2倍)。timeOffset
为本地时间增加一个偏移。 如果timeOffset是5s,那么本地时间的起始就是5s。
从parent time到active local time有一个公式,可以用来参考。
t = (tp - begin) * speed + offset
四、basic local time
这次转换是为了处理当前层级的重放(repeat)、以及重放之前是否要倒放(play backward)等操作。
比如当前层级是一个动画(CAAnimation遵守CAMediaTiming协议),duration是1s,经过第一次转换之后的active local time是5.5s。如果动画的repeatCount是10,那么经过第二次转化以后,basic local time会是0.5s,因此当前是动画展示一半的状态。
repeatCount及repeatDuration
当前的层级要重复的次数或重复的时间,两者不可同时指定。
以动画为例,如果指定repeatCount,那么指定了动画要重复几次。如果指定了repeatDuration,那么指定了动画重复的时间。autoreverses
在重复之前是否要倒放。
五、文首的例子
根据这些知识,可以解释文章开始时设置参数的效果。
当动画被加到layer上时,动画对应的time space被加到层级时间系统中,是layer对应的time space的子层级。
- 动画的速度是原来的两倍
设置动画的speed是2,这样子动画中的时间流逝速度时layer中时间流逝速度的2倍。当layer中时间经过0.5s时,动画中时间已经流逝了1s,动画已经完成了。(动画的duration是1s) - 点击开始动画的按钮,到开始动画,有一个延迟
我们首先得到了当前layer的时间addtime,然后把动画的begintime设置为addtime+0.3。这样子当动画被加到layer之后0.3s,layer中的时间是addtime+0.3,此时动画中的时间才开始计算,之前动画没有开始。 - 动画起始时,滑块的位置为中央,而不是在左边
我们设置了动画的offset为0.5s。当动画开始时,动画对应的time space的时间是0.5s,对应动画duration的一半,即滑块位置在屏幕中央。
六、更多应用
了解了CAMediaTiming协议后,可以实现很多动画的效果。
- 让某一个layer上的动画停止
设置layer的speed为0即可。 - 实现门打开然后关闭的效果
实现一个门打开的动画,然后把动画的autoreverses属性设置为YES即可。 - layer上的若干动画依次延迟启动
分别设置这些动画的beginTime为不同的值即可 - 手动控制动画的进度
设置动画的speed为0,然后改变动画的offset即可。
苹果已经把工具给我们了,可以做出什么样的产品就看大家的想象力了。
参考
控制动画时间
控制动画时间(上文的中文版)
Time Warp in Animation
iOS开发之动画中的时间(概况)的更多相关文章
- iOS开发之动画中的时间
概述 在动画中,我们会指定动画的持续时间.例如 scaleAnimation.duration = self.config.appearDuration 那么这个时间是怎么定义的呢?是指的绝对时间吗? ...
- iOS开发之动画编程的几种方法
iOS开发之动画编程的几种方法 IOS中的动画总结来说有五种:UIView<block>,CAAnimation<CABasicAnimation,CATransition,CAKe ...
- iOS 开发之动画篇 - 从 UIView 动画说起
毋庸置疑的:在iOS开发中,制作动画效果是最让开发者享受的环节之一.一个设计严谨.精细的动画效果能给用户耳目一新的效果,吸引他们的眼光 —— 这对于app而言是非常重要的. 本文作为动画文集的第一篇, ...
- iOS开发-动画总结
一.简介 IOS 动画主要是指Core Animation框架.官方使用文档地址为:Core Animation Guide.Core Animation是IOS和OS X平台上负责图形渲染与动画的基 ...
- ios开发核心动画七:核心动画与UIView动画的区别
/** UIView与核心动画区别?(掌握) 1.核心动画只作用在layer. 2.核心动画看到的都是假像,它并没有去修改UIView的真实位置. 什么时候使用核心动画? 1.当不需要与用户进行交互, ...
- iOS dateformatter设置GMT格式时间--iOS开发系列---项目中成长的知识四
今天在项目中开始接手客户端的签名这个模块,签名这个会在项目结束过后再单独写一下自己的心得! 今天讲讲在签名的过程中我们需要向服务器传送一个Date值,格式要求是格林威治时间,也就是GMT时间! 格式要 ...
- (转)iOS 开发,工程中混合使用 ARC 和非ARC
[前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...
- iOS 开发,工程中混合使用 ARC 和非ARC(转)
[前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...
- iOS开发CABasicAnimation动画理解
1.CALayer简介 CALayer是个与UIView很类似的概念,同样有backgroundColor.frame等相似的属性,我们可以将UIView看做一种特殊的CALayer.但实际上UIVi ...
随机推荐
- 【POJ1284】Primitive Roots
[题目大意] 给你一个素数n,求n的原根个数. [题解] 关于原根有一个定理: 定理:如果模有原根,那么它一共有个原根. 所以这题就是求phi[phi[n]] 很简单吧...(如果知道这个定理的话) ...
- Java方法重写与super关键字
----------siwuxie095 方法的重写: (1)在继承中也存在着重写的概念,其实就是子类定义了和父类同名的方法 (2)定义:方法名 ...
- 75-扩展GCD-时间复杂度
扩展gcd-时间复杂性 题目内容: 计算循环语句的执行频次 for(i=A; i!=B ; i+=C) x+=1; 其中A,B,C,i都是k位无符号整数. 输入描述 A B C k, 其中0<k ...
- [Training Video - 3] [Groovy in Detail] Non-static and Static variables, objects and object referances
log.info "starting" // we use class to create objects of a class Planet p1 = new Planet() ...
- AID-应用标识符的组成规则
AID:即唯一标识一个应用,分为两部分,RID(5字节)+PIX(最多11字节) RID:注册标识符,由ISO组织来分配,标识一个全球唯一的应用提供商,一般是分配给卡组织,比如分配给Master,比如 ...
- C#通过Redis实现分布式锁
Redis有三个最基本属性来保证分布式锁的有效实现: 安全性: 互斥,在任何时候,只有一个客户端能持有锁. 活跃性A:没有死锁,即使客户端在持有锁的时候崩溃,最后也会有其他客户端能获得锁,超时机制. ...
- Dubbo服务启动依赖检查
dubbo 官方文档地址:http://dubbo.io/User+Guide-zh.htm 项目中存在服务之间存在循环依赖,启动时总是报错,通过修改启动检查check=false解决,下面是dubb ...
- JS 观察者模式
Events = function() { var listen, log, obj, one, remove, trigger, __this; obj = {}; __this = this; l ...
- Android 最早使用的简单的网络请求
下面是最早从事android开发的时候写的网络请求的代码,简单高效,对于理解http请求有帮助.直接上代码,不用解释,因为非常简单. import java.io.BufferedReader; im ...
- vim的基本使用
Vim 编辑器中设置了三种模式—命令模式.末行模式和编辑模式,每种模式分别又支持多种不同的命令快捷键,这大大提高了工作效率,而且用户在习惯之后也会觉得相当顺手.要想高效率地操作文本,就必须先搞清这三种 ...