逐帧动画我们都会想到的是UIImageView,通过设置UIImageView的animationImages属性,然后调用它的startAnimating方法去播放这组图片。这种方法在某些场景下是可以达到逐帧的动画效果,但是这种方法一旦设置完图片中间的过程就无法控制了。可能有的想用利用iOS的定时器NSTimer定时更新图片来达到逐帧动画的效果。这种方式确实可以解决UIImageView一次性加载大量图片的问题,而且让播放过程可控,唯一的缺点就是定时器方法调用有时可能会因为当前系统执行某种比较占用时间的任务造成动画连续性出现问题。
虽然在核心动画没有直接提供逐帧动画类型,但是却提供了用于完成逐帧动画的相关对象CADisplayLink。CADisplayLink是一个计时器,但是同NSTimer不同的是,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒,这样一来使用它完成的逐帧动画(又称为“时钟动画”)完全感觉不到动画的停滞情况。
iOS程序在运行后就进入一个消息循环中(这个消息循环称为“主运行循环”),整个程序相当于进入一个死循环中,始终等待用户输入。将CADisplayLink加入到主运行循环队列后,它的时钟周期就和主运行循环保持一致,而主运行循环周期就是屏幕刷新周期。在CADisplayLink加入到主运行循环队列后就会循环调用目标方法,在这个方法中更新视图内容就可以完成逐帧动画。
当然这里不得不强调的是逐帧动画性能势必较低,但是对于一些事物的运动又不得不选择使用逐帧动画,例如人的运动,这是一个高度复杂的运动,基本动画、关键帧动画是不可能解决的。所大家一定要注意在循环方法中尽可能的降低算法复杂度,同时保证循环过程中内存峰值尽可能低。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) CALayer *mylayer;
@property (nonatomic, assign) int index;
@property (nonatomic, strong) NSMutableArray *images;
@end
@implementation ViewController
-(NSMutableArray *)images {
if(_images == nil) {
_images = [NSMutableArray array];
for(int i=1; i<=10; i++) {
[_images addObject:[UIImage imageNamed:[NSString stringWithFormat:@"a10%02d.jpg", i]]];
}
}
return _images;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 创建图像显示图层
self.mylayer = [[CALayer alloc] init];
self.mylayer.bounds = CGRectMake(0, 0, 80, 80);
self.mylayer.position = CGPointMake(160, 284);
[self.view.layer addSublayer:self.mylayer];
// 定义时钟对象
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(step)];
// 添加时钟对象到主运行循环
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
#pragma mark 每次屏幕刷新就会执行一次此方法(每秒接近60次)
-(void)step {
// 定义一个变量记录执行次数
static int s = 0;
// 每秒执行6次
if (++s%10 == 0) {
UIImage *image = self.images[self.index];
self.mylayer.contents = (id)image.CGImage; // 更新图片
self.index = (self.index + 1) % 10;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
动态图1.gif
网友评论