上次分享过一片关于NSTimer的知识,略有感触,然而与NSTimer有类似功能效果的还有一个控件CADisplayLink,长话短说,下面会对CADisplayLink进行一个简单而又系统的了解...
1: 什么是CADisplsyLink?
1)在屏幕刷新的时候使用:
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒(CADisplayLink 默认每秒运行60次,将它的frameInterval属性设置为2,意味CADisplayLink每隔一帧运行一次,有效的使游戏逻辑每秒运行30次)。
2)延迟
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
3)使用场景
CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
2: 代码分析
#import "CADisplayLinkController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface CADisplayLinkController ()
@property (nonatomic,strong) CADisplayLink* CADisplayLink;
@property (nonatomic,strong) UIButton* button;
@end
@implementation CADisplayLinkController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:self.button];
}
-(void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (_CADisplayLink)
{
[_CADisplayLink invalidate];
_CADisplayLink = nil;
}
}
-(UIButton*) button
{
if (!_button)
{
_button = [UIButton buttonWithType:UIButtonTypeCustom];
_button.frame = CGRectMake(SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT / 2, 200, 40);
_button.backgroundColor = [UIColor redColor];
_button.layer.cornerRadius = 5;
[_button setTitle:@"点击" forState:UIControlStateNormal];
[_button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _button;
}
-(CADisplayLink*) CADisplayLink
{
if (!_CADisplayLink)
{
/*创建一个_CADisplayLink对象,然后在target上绑定selector*/
_CADisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(startDisplayLink:)];
[_CADisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; /*将_CADisplayLink加入到RunLoop里面之后,selector就会被周期性的调用*/
}
return _CADisplayLink;
}
#pragma mark - 点击button
-(void) clickAction:(UIButton*) sender
{
[self CADisplayLink];
}
#pragma mark - 开始绘制
-(void) startDisplayLink:(id) sender
{
NSLog(@"startDisplayLink");
}
#pragma mark - touch屏幕,取消相应事件
-(void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[_CADisplayLink invalidate]; /*将CADisplayLink从RunLoop中移除,selector的调用也会停止*/
_CADisplayLink = nil; /*销毁CADisplayLink, 这样可以避免控制器不死*/
}
3:细节点
1) 关于暂停和开启设置
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
_CADisplayLink.paused = NO; /*开启, 类似于NSTimer的distantPast属性*/
}
-(void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_CADisplayLink.paused = YES; /*暂停属性,类似于NSTimer的distantFuture*/
}
2) 关于NSRunLoopMode
NSDefaultRunLoopModel:监听用户最基本的操作(点击,触摸等)
NSRunLoopCommonModels:监听一些特殊操作:滚动等
那么,为什么在NSDefaultRunLoopModel模式下发生滚动,计时器会停止?那是因为系统认为,用户不应该边滚动边操作界面,所以停止了(触碰,点击等)NSDefaultRunLoopModel模式下监听的事件
4:CADisplayLink与NSTimer之间的差异
1)精确度
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。
2)使用场合
CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用,比如用在背景计算,更新一些数值资料。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。
- 上面的一些基础知识,一些是自己在网上百度整合的,大概整理一下,关于如上的知识,几乎看完这些,差不多能有个大差不差的理解,谢谢您的阅读
参考知识:
NSRunLoopMode解析
NSTimer基础
官方文档
Core Animation系列之CADisplayLink