逐幀動畫我們都會想到的是UIImageView,通過設置UIImageView的animationImages屬性,然后調用它的startAnimating方法去播放這組圖片。這種方法在某些場景下是可以達到逐幀的動畫效果,但是這種方法一旦設置完圖片中間的過程就無法控制了。可能有的想用利用iOS的定時器NSTimer定時更新圖片來達到逐幀動畫的效果。這種方式確實可以解決UIImageView一次性加載大量圖片的問題,而且讓播放過程可控,唯一的缺點就是定時器方法調用有時可能會因為當前系統(tǒng)執(zhí)行某種比較占用時間的任務造成動畫連續(xù)性出現(xiàn)問題。
?雖然在核心動畫沒有直接提供逐幀動畫類型,但是卻提供了用于完成逐幀動畫的相關對象CADisplayLink。CADisplayLink是一個計時器,但是同NSTimer不同的是,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒,這樣一來使用它完成的逐幀動畫(又稱為“時鐘動畫”)完全感覺不到動畫的停滯情況。
?iOS程序在運行后就進入一個消息循環(huán)中(這個消息循環(huán)稱為“主運行循環(huán)”),整個程序相當于進入一個死循環(huán)中,始終等待用戶輸入。將CADisplayLink加入到主運行循環(huán)隊列后,它的時鐘周期就和主運行循環(huán)保持一致,而主運行循環(huán)周期就是屏幕刷新周期。在CADisplayLink加入到主運行循環(huán)隊列后就會循環(huán)調用目標方法,在這個方法中更新視圖內容就可以完成逐幀動畫。
?當然這里不得不強調的是逐幀動畫性能勢必較低,但是對于一些事物的運動又不得不選擇使用逐幀動畫,例如人的運動,這是一個高度復雜的運動,基本動畫、關鍵幀動畫是不可能解決的。所大家一定要注意在循環(huán)方法中盡可能的降低算法復雜度,同時保證循環(huán)過程中內存峰值盡可能低。
#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];
// 創(chuàng)建圖像顯示圖層
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)];
// 添加時鐘對象到主運行循環(huán)
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
#pragma mark 每次屏幕刷新就會執(zhí)行一次此方法(每秒接近60次)
-(void)step {
// 定義一個變量記錄執(zhí)行次數(shù)
static int s = 0;
// 每秒執(zhí)行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