利用CADisplayLink播放帧动画
来源:互联网 发布:淘宝400 bad request 编辑:程序博客网 时间:2024/04/27 23:29
UIImageView有个animationImages 属性可以逐帧播放动画 模仿UIImageView这个属性,利用CADisplayLink播放帧动画。
一、简介(原文地址:http://www.tuicool.com/articles/meMVR3)
1、所在框架
CADisplayLink和其它CoreAnimation类一样,都是在QuartzCore.framework里。
2、功能
CADisplayLink最主要的特征是能提供一个周期性的调用我们赋给它的selector的机制,从这点上看它很像定时器NSTimer。
3、使用方式
- (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不同的地方有:
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的几个重要属性:
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
打印timestamp值,其样式类似于:
179699.631584
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];NSLog("localLayerTime:%f",localLayerTime);
四、注意
1、CPU的空闲程度
如果CPU忙于其它计算,就没法保证以60HZ执行屏幕的绘制动作,导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。2、执行回调方法所用的时间
如果执行回调时间大于重绘每帧的间隔时间,就会导致跳过若干次回调调用机会,这取决于执行时间长短。//
// AnimationImageView.h
// 利用CADisplayLink播放动画
//
// Created by dengyanzhou on 15/1/30.
// Copyright (c) 2015年 mobby. All rights reserved.
//
#import <UIKit/UIKit.h>
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) {
//使动画停止
[_displayLinkinvalidate];
_displayLink =nil;
//计数重新归 0
_count =0;
_index =0;
animationComplete(NO);
return;
}
self.animationImageArray = animationArray;
self.animationTime = time;
_count =0;
_index =0;
//起定时器
self.displayLink = [CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(updateImage:)];
[self.displayLinkaddToRunLoop:[NSRunLoopmainRunLoop]forMode:NSDefaultRunLoopMode];
_tempAnimationComplete = animationComplete;
}
- (void)updateImage:(CADisplayLink*)displayLink{
//1s/60
if (++_count %self.transformatRate ==0 ) {
self.image = [UIImagegetPNGImage: [_animationImageArrayobjectAtIndex:_index]];
//动画完成
if (_index ==self.animationImageArray.count -1) {
[_displayLinkinvalidate];
_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 <UIKit/UIKit.h>
@interface ViewController :UIViewController
@end
//
// ViewController.m
// 利用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 {
[superviewDidLoad];
self.view.backgroundColor = [UIColorgrayColor];
imageView = [[AnimationImageViewalloc]initWithFrame:CGRectMake(100,200, 320,300)];
[self.viewaddSubview:imageView];
imageView.backgroundColor = [UIColorredColor];
imageArray = [NSMutableArrayarray];
for (int i =1; i <= 68; i++) {
// NSString *imageName = @"7-8_Game2_bulusi 300";
NSString *imageName = [NSStringstringWithFormat:@"7-8_Game2_bulusi 300%02d",i];
// UIImage *image = [UIImage imageNamed:imageName];
[imageArrayaddObject:imageName];
}
UIButton *button = [UIButtonbuttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(700,400, 200,80);
button.backgroundColor = [UIColorblueColor];
[button setTitle:@"点击播放"forState:UIControlStateNormal];
[button addTarget:selfaction:@selector(buttonClick)forControlEvents:UIControlEventTouchUpInside];
[self.viewaddSubview: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] ;
[imageViewplayAnimationWithImageArray: imageArray durationTime:3complete:^(BOOL isFinished) {
if (isFinished) {
NSLog(@"动画完成");
}else{
NSLog(@"动画没有完成");
}
}];
}
- (void)didReceiveMemoryWarning {
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
- 利用CADisplayLink播放帧动画
- CADisplayLink 动画
- 利用CADisplayLink来实现一些碉堡的动画
- iOS CoreAnimation 逐帧动画 CADisplayLink
- 动画黄金搭档:CADisplayLink & CAShapeLayer
- 动画黄金搭档:CADisplayLink & CAShapeLayer
- 动画黄金搭档:CADisplayLink & CAShapeLayer
- 动画黄金搭档:CADisplayLink&CAShapeLayer
- 利用TweenPosition 动画制作直角动画播放。
- UIBezierPath、CADisplayLink实现波浪动画
- [转]动画黄金搭档:CADisplayLink & CAShapeLayer
- iOS CADisplayLink(绘制动画)
- 利用Animate Control 播放动画失败!!
- CoCos2d-x利用库函数进行动画播放
- 利用UIImageView连续播放图片动画效果
- VB利用PictureBox控件播放GIF动画
- 帧动画自动播放
- sprite帧动画播放
- Selenium 1.0 - Metrics Testing - xPath正确但是click方法没有反应
- Head First JQuery学习笔记(三)
- 【原】移动web资源整理
- 程序员收集整理的PHP资源大全
- iOS入门-了解这些常用单词,再也不怕看不懂苹果代码以及api接口了
- 利用CADisplayLink播放帧动画
- mysql优化小记(持续更新)
- [OpenSource]三代开源社区的协作模式
- QT,QT SDK, QT Creator 区别
- 最近ELK(elasticsearch+logstash+kibana)学习小结
- SOA,SOAP,WSDL,UDDI,XML
- 【ACM之旅】瓷砖铺放
- css的框模型速查
- MyActivity