UIImageView有个animationImages 属性可以逐帧播放动画 模仿UIImageView这个属性,利用CADisplayLink播放帧动画。
CADisplayLink和其它CoreAnimation类一样,都是在QuartzCore.framework里。
CADisplayLink最主要的特征是能提供一个周期性的调用我们赋给它的selector的机制,从这点上看它很像定时器NSTimer。
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
//do something
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
当把CADisplayLink对象add到runloop中后,selector就能被周期性调用,类似于NSTimer被启动了;执行invalidate操作时, CADisplayLink对象就会从runloop中移除,selector 调用也随即停止,类似于NSTimer的invalidate方法。
下面结合NSTimer来介绍 CADisplayLink,与NSTimer不同的地方有:
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop后, 每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。
NSTimer以指定的模式注册到runloop后,每当设定的周期时间到达后,runloop会向指定的target发送一次指定的selector消息。
iOS设备的屏幕刷新频率(FPS)是60Hz,因此CADisplayLink的selector 默认调用周期是每秒60次,这个周期可以通过frameInterval属性设置, CADisplayLink的selector每秒调用次数=60/ frameInterval。比如当 frameInterval设为2,每秒调用就变成30次。因此, CADisplayLink 周期的设置方式略显不便。
NSTimer的selector调用周期可以在初始化时直接设定,相对就灵活的多。
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。
NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在忙于别的调用,触发时间就会推迟到下一个runloop周期。更有甚者,在OS X v10.9以后为了尽量避免在NSTimer触发时间到了而去中断当前处理的任务,NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间范围。
从原理上不难看出, CADisplayLink 使用场合相对专一, 适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。
下面不完整的列出了 CADisplayLink的几个重要属性:
可读可写的NSInteger型值,标识间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。官方文档中强调,当该值被设定小于1时,结果是不可预知的。
只读的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 。
179699.631584
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog("localLayerTime:%f",localLayerTime);
//
// AnimationImageView.h
// 利用CADisplayLink播放动画
//
// Created by dengyanzhou on 15/1/30.
// Copyright (c) 2015年 mobby. All rights reserved.
//
#import
typedef void (^AnimationComplete) (BOOL isFinished);
@interface AnimationImageView : UIImageView
@property(nonatomic)int step;
@property(nonatomic,strong)NSArray *animationImageArray;//要播放的图片帧数组
@property(nonatomic,copy)AnimationComplete tempAnimationComplete;
@property(nonatomic,assign)int index; //定位当前播放的图片
@property(nonatomic)float animationTime; // 动画持续时间
@property(nonatomic)int count;
@property(nonatomic )float frequency; // 频率
@property(nonatomic) int transformatRate; //转化率
@property(nonatomic,strong)CADisplayLink *displayLink; // 定时器
- (void)playAnimationWithImageArray:(NSArray*)animationArray durationTime:(float)time complete:(AnimationComplete)animationComplete;
@end
// 实现文件
//
// AnimationImageView.m
// 利用CADisplayLink播放动画
//
// Created by dengyanzhou on 15/1/30.
// Copyright (c) 2015年 mobby. All rights reserved.
//
#import "AnimationImageView.h"
#import "UIImageAdditions.h"
@implementation AnimationImageView
- (void)playAnimationWithImageArray:(NSArray*)animationArray durationTime:(float)time complete:(AnimationComplete)animationComplete{
// 动画播放数组为空 直接返回 动画没有完成;
if (!animationArray.count ) {
animationComplete(NO);
return;
}
//时间为零 直接返回
if (time <= 0) {
return;
}
self.frequency = time/animationArray.count;
self.transformatRate = (int)(self.frequency * 60);
if (self.transformatRate == 0) {
self.transformatRate = 1;
}
//_disPlayelink 还存在表明动画没有完成
if (self.displayLink) {
//使动画停止
[_displayLink invalidate];
_displayLink = nil;
//计数 重新归 0
_count = 0;
_index = 0;
animationComplete(NO);
return;
}
self.animationImageArray = animationArray;
self.animationTime = time;
_count = 0;
_index = 0;
//起 定时器
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateImage:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop]forMode:NSDefaultRunLoopMode];
_tempAnimationComplete = animationComplete;
}
- (void)updateImage:(CADisplayLink*)displayLink{
//1s/60
if (++_count % self.transformatRate == 0 ) {
self.image = [UIImage getPNGImage: [_animationImageArray objectAtIndex:_index]];
//动画完成
if (_index == self.animationImageArray.count - 1) {
[_displayLink invalidate];
_displayLink = nil;
self.animationImageArray = nil;
// 动画完成 block回调
self.tempAnimationComplete(YES);
}
_index++;
}
}
@end
//在viewContro 里实例化AnimationImageView 并调用
AnimationImageView对象的
- (void)playAnimationWithImageArray:(NSArray*)animationArray durationTime:(float)time complete:(AnimationComplete)animationComplete 方法
//
// ViewController.h
// 利用CADisplayLink播放动画
//
// Created by dengyanzhou on 15/1/30.
// Copyright (c) 2015年 mobby. All rights reserved.
//
#import
@interface ViewController : UIViewController
@end
//
// 利用CADisplayLink播放动画
//
// Created by dengyanzhou on 15/1/30.
// Copyright (c) 2015年 mobby. All rights reserved.
//
#import "ViewController.h"
#import "AnimationImageView.h"
@interface ViewController (){
NSMutableArray *imageArray;
AnimationImageView *imageView;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
imageView = [[AnimationImageView alloc]initWithFrame:CGRectMake(100, 200, 320, 300)];
[self.view addSubview:imageView];
imageView.backgroundColor = [UIColor redColor];
imageArray = [NSMutableArray array];
for (int i = 1; i <= 68; i++) {
// NSString *imageName = @"7-8_Game2_bulusi 300";
NSString *imageName = [NSString stringWithFormat:@"7-8_Game2_bulusi 300%02d",i];
// UIImage *image = [UIImage imageNamed:imageName];
[imageArray addObject:imageName];
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(700, 400, 200, 80);
button.backgroundColor = [UIColor blueColor];
[button setTitle:@"点击播放" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
#if !__has_feature(objc_arc) // 如果不是arc
[imageArray relese];
#endif
// Do any additional setup after loading the view, typically from a nib.
}
- (void)buttonClick{
// imageView.animationImages = [NSArray arrayWithArray:imageArray];
// imageView.animationRepeatCount = 1;
// imageView.animationDuration = 4.0f;
// [imageView startAnimating] ;
[imageView playAnimationWithImageArray: imageArray durationTime:3 complete:^(BOOL isFinished) {
if (isFinished) {
NSLog(@"动画完成");
}else{
NSLog(@"动画没有完成");
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end