CADisplayLink

CADisplayLink

CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。我们在应用中创建一个新的 CADisplayLink 对象,把它添加到一个runloop中,并给它提供一个 target 和selector 在屏幕刷新的时候调用。

一但 CADisplayLink 以特定的模式注册到runloop之后,每当屏幕需要刷新的时候,runloop就会调用CADisplayLink绑定的target上的selector,这时target可以读到 CADisplayLink 的每次调用的时间戳,用来准备下一帧显示需要的数据。例如一个视频应用使用时间戳来计算下一帧要显示的视频数据。在UI做动画的过程中,需要通过时间戳来计算UI对象在动画的下一帧要更新的大小等等。 在添加进runloop的时候我们应该选用高一些的优先级,来保证动画的平滑。可以设想一下,我们在动画的过程中,runloop被添加进来了一个高优先级的任务,那么,下一次的调用就会被暂停转而先去执行高优先级的任务,然后在接着执行CADisplayLink的调用,从而造成动画过程的卡顿,使动画不流畅。

属性和方法

方法:

//生成实例
+(CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
//将实例加入到一个选定的runloop中,事件就能被触发了。
-(void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
//从某个runloop中移除当前实例
-(void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
//计时器销毁
-(void)invalidate;

属性:

  • timestamp,获取上一次selector被执行的时间戳。这个属性是一个只读属性,而且你要记住的是只有当selector被执行过一次之后这个值才会被取到有效值。这个属性同上是用来比较当前图层时间与上一次selector执行时间只差,从而来计算本次UI应该发生的改变的进度(例如视图做移动效果)。

  • duration,获取当前设备的屏幕刷新时间间隔。同timestamp一样,他也是个只读属性,并且也需要selector触发一次才可以取值。值的一提的是,当前iOS设备的刷新频率都是60HZ。也就是说每16.7ms刷新一次。作用也与timestamp相同,都可以用于辅助计算。它只是个大概的时间,如果CPU过于繁忙,duration的值是会浮动的.这样就没法保证以相同的频率执行屏幕的绘制操作,这样会跳过几次调用回调方法的机会。

  • paused,控制计时器暂停与恢复的属性。设置为YES的时候会暂停事件的触发。想结束一个CADisplayLink的时候,应该调用 invalidate

  • frameInterval,事件触发间隔。是指两次selector触发之间间隔几次屏幕刷新,默认值为1,即每帧都调用一次selector,这个也可以间接用来控制动画速度。

CADisplayLink 与 NSTimer 有什么不同

iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。 NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且 NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。 CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。

tips

  • 两次selector触发的时间间隔是time = frameInterVal * duration。selector执行所需要的时间一定要小于其触发间隔,否则会造成掉帧情况。 通常来讲iOS设备的刷新频率事60HZ也就是每秒60次。那么每一次刷新的时间就是1/60秒 大概16.7毫秒。当我们的frameInterval值为1的时候我们需要保证的是 CADisplayLink调用的`target`的函数计算时间不应该大于 16.7否则就会出现严重的丢帧现象。

  • CADisplayLink 不能被继承。

优势:

依托于设备屏幕刷新频率触发事件,所以其触发时间上是最准确的。也是最适合做UI不断刷新的事件,过渡相对流畅,无卡顿感。

缺点:

由于依托于屏幕刷新频率,若果CPU不堪重负而影响了屏幕刷新,那么触发事件也会受到相应影响。 selector触发的时间间隔只能是duration的整倍数。 selector事件如果大于其触发间隔就会造成掉帧现象。