一、简介
1、所在框架
CADisplayLink
和其它CoreAnimation类一样,都是在QuartzCore.framework
里。
2、功能
CADisplayLink
最主要的特征是能提供一个周期性的调用我们赋给它的selector
的机制,从这点上看它很像定时器NSTimer
。
3、使用方式
-(void)setupDisplay{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateTextColor:)];
self.displayLink.paused = YES;
self.displayLink.frameInterval = 60.0;
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
-(void)updateTextColor:(CADisplayLink *)displayLink{
NSLog(@"%f",displayLink.timestamp);
}
- (void)startAnimation{
self.displayLink.paused = NO;
}
- (void)stopAnimation{
self.displayLink.paused = YES;
[self.displayLink invalidate];
self.displayLink = nil;
}
当把CADisplayLink
对象add到runloop
中后,selecto
r就能被周期性调用,类似于NSTimer被启动了;执行invalidate
操作时, CADisplayLink对象就会从runloop
中移除,selector
调用也随即停止,类似于NSTimer的invalidate
方法。
二、特性
下面结合NSTimer
来介绍 CADisplayLink
,与NSTimer不同的地方有:
1、原理不同
CADisplayLink
是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop
后, 每当屏幕显示内容刷新结束的时候,runloop
就会向 CADisplayLink指定的target
发送一次指定的selector
消息,CADisplayLink类对应的selector就会被调用一次
。NSTimer
以指定的模式注册到runloop
后,每当设定的周期时间到达后,runloo
p会向指定的target发送一次指定的selector
消息。
2、周期设置方式不同
iOS设备的屏幕刷新频率(FPS)
是60Hz
,因此CADisplayLink的selector 默认调用周期是每秒60次
,这个周期可以通过frameInterval
属性设置, CADisplayLink的selector每秒调用次数=60/ frameInterval
。比如当 frameInterval设为2,每秒调用就变成30次。因此, CADisplayLink 周期的设置方式略显不便。
NSTimer
的selector
调用周期可以在初始化时直接设定,相对就灵活的多。
3、精确度不同
iOS
设备的屏幕刷新频率是固定的,CADisplayLink
在正常情况下会在每次刷新结束都被调用,精确度相当高
。
NSTimer
的精确度就显得低了
点,比如NSTimer的触发时间到的时候,runloop
如果在忙于别的调用,触发时间就会推迟到下一个runloop周期。更有甚者,在OS X v10.9以后为了尽量避免在NSTimer
触发时间到了而去中断当前处理的任务,NSTimer新增了tolerance
属性,让用户可以设置可以容忍的触发的时间范围。
4、使用场合
从原理上不难看出,CADisplayLink
使用场合相对专一, 适合做界面的不停重绘
,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
NSTimer
的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。
三、重要属性
下面不完整的列出了 CADisplayLink的几个重要属性:
1、 frameInterval
可读可写的NSInteger
型值,标识间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。官方文档中强调,当该值被设定小于1时,结果是不可预知的。
2、duration
只读的CFTimeInterval
值,表示两次屏幕刷新之间的时间间隔
。需要注意的是,该属性在target
的selector
被首次调用以后才会被赋值。selector
的调用间隔时间计算方式是:时间=duration×frameInterval
。
现存的iOS设备屏幕的FPS都是60Hz
,这一点可以从CADisplayLink
的duration
属性看出来。duration的值都是0.166666…,即1/60
。尽管如此,我们并没法确定苹果不会改变 FPS ,如果以后某一天将 FPS 提升到了120Hz了怎么办呢?这时,你设置了frameInterval属性值为2期望每秒刷新30次,却发现每秒刷新了60次,结果可想而知,出于安全考虑,还是先根据duration判断屏幕的 FPS再去使用 CADisplayLink 。
3、timestamp
只读的CFTimeInterval
值,表示屏幕显示的上一帧的时间戳,这个属性通常被target用来计算下一帧中应该显示的内容。
打印timestamp值,其样式类似于:
29846.767264
29846.783930
29846.800597
29846.817264
29846.833930
29846.850597
29846.867264
虽然名为时间戳,但这和常见的unix时间戳差异很大,事实上这是CoreAnimation使用的时间格式。每个CALayer都有一个本地时间(CALayer本地时间的具体作用会在后续文章中说明),可以获取当前CALayer的本地时间并打印:
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog("localLayerTime:%f",localLayerTime);
四、注意
iOS并不能保证能以每秒60次的频率调用回调方法,这取决于:
1、CPU的空闲程度
如果CPU
忙于其它计算,就没法保证以60HZ
执行屏幕的绘制动作,导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
2、执行回调方法所用的时间
如果执行回调时间大于重绘每帧的间隔时间
,就会导致跳过若干次回调调用机会,这取决于执行时间长短。
五、参考文档
1、官方文档
https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html#//apple_ref/doc/uid/TP40009031-CH1-DontLinkElementID_1
六、实例
GitHub: https://github.com/iOSlixiang/Animations.git