iOS[QuartzCore框架]CADisplayLink篇

96
鲸鱼Alice1992
2016.05.31 17:38* 字数 1279

在学习CoreAnimation的时候,发现好多地方都用到了CADisplayLink,现在就整理下自己的笔记吧,不足之处敬请指正。
======================================
一、简介
CADisplayLink简单的说就是一个定时器,每隔几毫秒刷新一次屏幕。CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。我们在应用中创建一个新的CADisplayLink对象,把它添加到一个runloop中,并给它提供一个target和selector在屏幕刷新的时候调用。另外CADisplayLink 不能被继承。

二、NSTimer和CADisplayLink
既然CADisplayLink也是一个定时器,那么两者有什么区别与联系呢?
1、原理不同
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。
NSTimer以指定的模式注册到runloop后,每当设定的周期时间到达后,runloop会向指定的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.h的常用属性和方法
1、创建一个CADisplayLink对象
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
2、将CADisplayLink对象添加到runloop中
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
3、将CADisplayLink对象从runloop中删除
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
4、结束CADisplayLink
- (void)invalidate;
5、每帧之间的时间
@property(readonly, nonatomic) CFTimeInterval duration;
6、间隔多少帧调用一次selector 方法,默认1
@property(nonatomic) NSInteger frameInterval;
7、屏幕显示的上一帧的时间戳,只读的CFTimeInterval值。
@property(readonly, nonatomic) CFTimeInterval timestamp;
8、是否暂停,控制CADisplayLink的运行
@property(getter=isPaused, nonatomic) BOOL paused;

四、注意
1、通常来讲:iOS设备的刷新频率事60HZ也就是每秒60次。那么每一次刷新的时间就是1/60秒 大概16.7毫秒。当我们的frameInterval 值为1的时候我们需要保证的是 CADisplayLink调用的target的函数计算时间不应该大于 16.7否则就会出现严重的丢帧现象。 在mac应用中我们使用的不是CADisplayLink而是 CVDisplayLink它是基于C接口的用起来配置有些麻烦但是用起来还是很简单的。
2、iOS并不能保证能以每秒60次的频率调用回调方法,这取决于:
(1)CPU的空闲程度
如果CPU忙于其它计算,就没法保证以60HZ执行屏幕的绘制动作,导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
(2)执行回调方法所用的时间
如果执行回调时间大于重绘每帧的间隔时间,就会导致跳过若干次回调调用机会,这取决于执行时间长短。

五、实例
我们利用CAShapeLayer和贝塞尔曲线UIBezierPath还有CADisplayLink等实现一个弹性动画效果。
demo地址:https://github.com/Resory/RYCuteView
效果如图:

1451900088765490.gif

1、 首先看下这个动画实现的逻辑思路
1451900102783370.png

(1)图中蓝色部分是一个CAShapeLayer,它的形状由UIBezierPath的路径组成。
(2)这个路径是有r1,r2,r3,r4,r5这5个红点确定。其中r1,r2,r3,r4都是不动点,唯一可以动的是r5点。
(3)根据动态图可以看出CAShapeLayer的形状是随着r5红点的移动而相应变化的,所以只要获得r5的坐标变化就可以用UIBezierPath做出相应的路径,然后就可以形成相应的形状。
2、实现
(1)初始化CAShapeLayer
屏幕快照 2016-05-31 下午5.32.52.png

(2)初始化r5点
FC4FCE6D-2A16-4734-ABA7-7FA45CFFFFE5.png

(3)添加移动手势&CADisplayLink
AA778F29-0933-4C82-B1BB-1CBD8D794E9E.png

(4)手势解析
手势移动时,r5红点跟着手势移动,_shapeLayer则根据r5的坐标来扩大自己的区域
手势结束时,r5红点通过UIView的动画方法来改变r5的坐标,同时_shapeLayer根据r5的坐标缩小自己的区域并最终返回原形。
09EB9484-BB8F-41B6-B5F2-3ECD70763731.png

(5)根据r5的位置,更新_shapeLayer形状
0AA97B2F-D1A2-4CFE-92C5-F8EA1F8FE1F7.png

(6)计算弹簧效果坐标
2AF1A988-8FFF-472A-BC4E-DD96C5679C73.png

感谢:http://www.cocoachina.com/ios/20151231/14823.html

iOS
Web note ad 1