前面我們介紹了Core Animation框架中的圖層拆檬,接下來我們來學習動畫部分于樟,動畫是Core Animation一個顯著的特性务冕。要注意的是书释,Core Animation的動畫執(zhí)行過程都是在后臺操作的翘贮,不會阻塞主線程。且Core Animation是直接作用在CALayer上的爆惧,并非UIView狸页。
Core Animation對CALayer所有的可動畫屬性做動畫。動畫不需要我們在Core Animation手動打開检激,除非明確關(guān)閉肴捉,否則它一直存在。這也是存在隱式動畫的原因(當然也有顯式動畫)叔收。
我們先從隱式動畫來了解動畫的本質(zhì)齿穗,再進一步學習顯式動畫。
一. 隱式動畫
隱式動畫是因為我們沒有指定動畫類型饺律,僅僅改變一個屬性窃页,然后Core Animation決定如何并且何時去執(zhí)行動畫,實際上動畫執(zhí)行的時間取決于當前事務(wù)的設(shè)置复濒,動畫類型取決于圖層行為脖卖。
事務(wù)
事務(wù)是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務(wù)去改變可以做動畫的圖層屬性不會馬上改變巧颈,而是當事務(wù)提交的時候開始用一個動畫過渡到新值畦木。
事務(wù)是通過CATransaction類管理的,你可以把CATransaction當成一個事務(wù)棧(管理類)砸泛。CATransaction沒有屬性和實例方法十籍,也不能被主動創(chuàng)建蛆封,只能用+began和+commit分別來入棧和出棧。
任何可以做動畫的圖層屬性都會被添加到棧頂?shù)氖聞?wù)勾栗,事務(wù)默認的動畫時間是0.25秒(你也可以重新設(shè)置惨篱,不過建議起一個新的事務(wù),以免影響該事務(wù)的其他動畫)围俘。
在某次RunLoop中設(shè)置了一個可動畫圖層屬性砸讳,如果當前沒有設(shè)置事務(wù),Core Animation會自動創(chuàng)建一個事務(wù)界牡,并在當前線程的下一個RunLoop中commit這個事務(wù)簿寂。這個也是常說的隱形事務(wù)(沒有主動去創(chuàng)建),當然顯示事務(wù)就是通過明確的調(diào)用begin,commit來提交動畫欢揖。
實際上陶耍,我們常用的UIView的-animateWithDuration:animations:也是一個原理,CATransaction的+began和+commit會在-animateWithDuration:animations:內(nèi)部自動調(diào)用她混,這樣block中所有屬性的改變都會被事務(wù)所包含烈钞。另外CATranscation也提供了一個完成塊的接口,類似于UIView的-animateWithDuration:animations:completion:坤按。完成塊是在該個事務(wù)提交并出棧后才被執(zhí)行的(即上一個任務(wù)完成)毯欣,然后添加進默認的事務(wù)(時間0.25s)。
- (void)createCATransaction{
//開始一個新事務(wù)
[CATransaction begin];
//設(shè)置該事務(wù)時間為2s
[CATransaction setAnimationDuration:2.0];
//添加事務(wù)完成后執(zhí)行的Block(該Block不在該事務(wù)執(zhí)行)
[CATransaction setCompletionBlock:^{
//旋轉(zhuǎn)90度--------任務(wù)2(耗時0.25s)
CGAffineTransform transform = self.colorLayer.affineTransform;
transform = CGAffineTransformRotate(transform, M_PI_2);
self.colorLayer.affineTransform = transform;
}];
//隨機切換顏色--------任務(wù)1執(zhí)行(耗時2s)再執(zhí)行任務(wù)2(耗時0.25s)
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//提交事務(wù)
[CATransaction commit];
}
圖層行為
當我們修改UIView關(guān)聯(lián)的圖層上的可動畫屬性時臭脓,會發(fā)現(xiàn)沒有平滑過渡的動畫酗钞。因為UIView把它關(guān)聯(lián)的圖層的隱式動畫特性關(guān)閉了。
我們先來了解一些隱式動畫在視圖上是怎么實現(xiàn)的来累。我們把改變屬性時CALayer自動應(yīng)用的動畫稱為行為砚作,當CALayer的屬性被改變時,它會調(diào)用-actionForKey:方法嘹锁,傳遞屬性名稱葫录,獲取動畫。
具體的步驟如下:
① 圖層首先檢測它是否有委托领猾,并且是否實現(xiàn)CALayerDelegate的- actionForLayer:forKey米同。如果有,直接調(diào)用并返回結(jié)果摔竿。
② 如果沒有委托面粮,或委托沒有實現(xiàn)- actionForLayer:forKey方法,圖層會接著檢查屬性名稱對應(yīng)行為映射的actions字典继低。
③ 如果actions字典沒有包含對應(yīng)的屬性熬苍,圖層接著在stytle字典搜索屬性名。
④ 如果stytle字典也找不到對應(yīng)的行為袁翁,圖層將直接調(diào)用定義了每個屬性的標準行為的-defaultActionForKey:方法柴底。
而UIView就是通過返回 - actionForLayer:forKey為 nil 來禁用隱式動畫钱磅。當然在動畫塊中(事務(wù)中),會根據(jù)屬性返回行為( 行為通常是被Core Animation隱式調(diào)用的顯式動畫對象似枕,比如CABasicAnimation),然后CALayer拿這個結(jié)果去對先前和當前的值做動畫年柠。
//傳入屬性凿歼,獲取行為
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;
當然冗恨,禁用隱式動畫答憔,也可以通過設(shè)置CATransacition,用來對里面所有屬性關(guān)閉開啟隱式動畫掀抹。
[CATransaction begin];
[CATransacition setDisableActions:YES];
...
[CATransaction commit];
總結(jié)一下:
- UIView關(guān)聯(lián)的圖層禁用了隱式動畫虐拓,對這種圖層做動畫主要有三種方式:
① 使用UIView的動畫函數(shù)(而不是依賴CATransaction)
② 繼承UIView,并覆蓋 - actionForLayer:forKey傲武。
③ 直接創(chuàng)建一個顯式動畫蓉驹。 - 對于單獨的圖層,我們可以通過實現(xiàn)圖層的- actionForLayer:forKey委托方法揪利,或者提供actions字典來控制隱式動畫态兴。
呈現(xiàn)過程
當改變圖層的屬性時,屬性值是馬上更新的疟位,但屏幕上沒有馬上改變瞻润。這是因為設(shè)置的屬性沒有直接調(diào)整圖層的外觀,它只是定義圖層動畫結(jié)束之后要展示的外觀甜刻。
當設(shè)置CALayer的屬性绍撞,實際上是在定義當前事務(wù)結(jié)束之后圖層如何顯示的模型。這邊可以看做一個MVC得院,Core Animation扮演控制器的角色傻铣,負責根據(jù)圖層行為和事務(wù)設(shè)置去不斷更新視圖這些屬性在屏幕上的變化,CALayer是存儲視圖如何顯示和動畫的模型尿招,用戶界面就是MVC中的view矾柜。在蘋果開發(fā)文檔中,圖層樹通常都是值的圖層樹模型就谜。
在iOS中怪蔑,屏幕每秒重繪60次,如果動畫時長大于1/60秒丧荐,Core Animation就要設(shè)置一個中間值(顯示值)來對屏幕上的圖層重新組織缆瓣。每個圖層屬性的顯示值被存儲在呈現(xiàn)圖層中,這個呈現(xiàn)圖層是獨立的虹统,實際上是模型圖層(原來的圖層)的復(fù)制弓坞。你可以通過呈現(xiàn)圖層的值來獲取當前屏幕上真正顯示出來的值隧甚。對應(yīng)的還有呈現(xiàn)樹,呈現(xiàn)樹由圖層樹中所有圖層的呈現(xiàn)圖層形成渡冻。有點需要注意的是戚扳,呈現(xiàn)圖層僅當圖層首次被提交的時候創(chuàng)建,之前的話是nil族吻。
圖層調(diào)用-presentationLayer返回呈現(xiàn)圖層帽借,呈現(xiàn)圖層調(diào)用-modelLayer返回原本的圖層(模型圖層)。
大多數(shù)情況下超歌,我們不需要訪問呈現(xiàn)圖層砍艾,只需要和模型圖層進行交換。但以下情況呈現(xiàn)圖層卻很重要:
- 實現(xiàn)基于定時器的動畫巍举。準確知道某一時刻圖層顯示在什么位置對正確擺放圖層很有用脆荷。
- 想讓做動畫的圖層響應(yīng)用戶輸入。這是要對呈現(xiàn)圖層使用-hitTest:,因為這代表了用戶看到的圖層位置懊悯。
@interface ViewController ()
@property (nonatomic, strong) CALayer *colorLayer;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create a red layer
self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(0, 0, 100, 100);
self.colorLayer.position = CGPointMake(self.view.bounds.size.width / 2,
self.view.bounds.size.hei self.colorLayer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:self.colorLayer];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//get the touch point
CGPoint point = [[touches anyObject] locationInView:self.view];
//check if we've tapped the moving layer
if ([self.colorLayer.presentationLayer hitTest:point]) {
//randomize the layer background color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].
} else {
//otherwise (slowly) move the layer to new position
[CATransaction begin];
[CATransaction setAnimationDuration:4.0];
self.colorLayer.position = point;
[CATransaction commit];
}
}
@end
二. 顯式動畫
顯式和隱式的區(qū)別蜓谋,無非就是隱式的系統(tǒng)幫我們做了,而顯式的需要我們自己去調(diào)用炭分,隱式的行為也是調(diào)用顯式動畫對象(比如CABasicAnimation)孤澎。
要想執(zhí)行動畫,就必須初始化一個CAAnimation對象欠窒。CAAnimation是所有動畫類的父類覆旭,但是它不能直接使用,應(yīng)該使用它的子類岖妄。
CAAnimation有三個子類:屬性動畫(該類不能直接用型将,需要用它的子類),動畫組荐虐,過渡動畫七兜。我們從這幾個子類一一介紹起。
① 屬性動畫(CAPropertyAnimation)
CAPropertyAnimation是CAAnimation的子類福扬,但是不能直接使用腕铸,要想創(chuàng)建動畫對象,應(yīng)該使用它的兩個子類:基礎(chǔ)動畫(CABasicAnimation)和關(guān)鍵幀動畫(CAKeyframeAnimation)铛碑。
它有個NSString類型的keyPath屬性狠裹,你可以指定CALayer的某個屬性名為keyPath,并且對CALayer的這個屬性的值進行修改汽烦,達到相應(yīng)的動畫效果涛菠。比如,指定@"position"為keyPath,就會修改CALayer的position屬性的值俗冻,以達到平移的動畫效果礁叔。也可以設(shè)置子屬性或者虛擬屬性(比如“transform.rotation”)。主要根據(jù)情況來迄薄,比如使用“transform.rotation”就不會和“transform.position”或者“transform.scale”沖突琅关,相對于“transform”只需要給toValue賦一個簡單的數(shù)值。
基礎(chǔ)動畫
使用CABasicAnimation可以實現(xiàn)一些基本的動畫效果讥蔽,它可以讓CALayer的某個屬性從某個值漸變到另一個值死姚。不過執(zhí)行動畫后keyPath的值會迅速回到原始值,需要我們手動去設(shè)置勤篮。一種是使用fillMode。
- (void)createCABasicAnimation{
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
CABasicAnimation *baseicAnimation = [CABasicAnimation animation];
baseicAnimation.keyPath = @"backgroundColor";
baseicAnimation.toValue = (__bridge id _Nullable)(color.CGColor);
//保持動畫執(zhí)行后的狀態(tài)
baseicAnimation.removedOnCompletion = NO;
baseicAnimation.fillMode = kCAFillModeForwards;
[self.colorLayer addAnimation:baseicAnimation forKey:nil];
}
一種是在-animationDidStop:finished:方法中設(shè)置該值(遵守CAAnimationDelegate協(xié)議)色罚,這種方法相對麻煩多碰缔,多個屬性情況下需要判斷。
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag {
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}
關(guān)鍵幀動畫
相較于CABasicAnimation簡單的功能戳护,CAKeyframeAnimation雖然也是作用于單一元素金抡,但它不限制于設(shè)置一個其實和結(jié)束的值,而是可以根據(jù)一連串隨意的值來做動畫腌且。它主要有兩種運用:一種是設(shè)置values:
- (void)createCAKeyframeAnimation{
CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animation];
keyAnimation.duration = 3;
keyAnimation.keyPath = @"backgroundColor";
//需要說明的是梗肝,動畫開始時它會突然變到第一個顏色,因為它把第一個顏色當成第一幀铺董,動畫結(jié)束時又會突然恢復(fù)原來的值巫击。
//所以為了動畫的平滑特性,關(guān)鍵幀的值要設(shè)置好精续。
keyAnimation.values = @[(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor yellowColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor
];
[self.colorLayer addAnimation:keyAnimation forKey:nil];
}
另一種是設(shè)置path(比如view根據(jù)path做平移動畫):
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 4.0;
animation.path = bezierPath.CGPath;//設(shè)置路徑
animation.rotationMode = kCAAnimationRotateAuto;//根據(jù)曲線的切線自動旋轉(zhuǎn)
[shipLayer addAnimation:animation forKey:nil];
② 動畫組(CAAnimationGroup)
CAAnimationGroup添加了一個animations數(shù)組的屬性坝锰,用來組合別的動畫。
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[animation1, animation2];
groupAnimation.duration = 4.0;
[colorLayer addAnimation:groupAnimation forKey:nil];
③ 過渡動畫(CATransition)
屬性動畫只對圖層的可動畫屬性起作用重付,如果要改變一個不可動畫的屬性顷级,或者從層級關(guān)系中移除添加圖層,就需要用到CATransition确垫。CATransition主要用于過渡弓颈,其中有兩個重要的屬性:type和subtype。
//type有四種類型:
kCATransitionFade;//默認值删掀,淡入淡出
kCATransitionPush;//從一側(cè)滑動進來翔冀,把舊圖層推出去
kCATransitionMoveIn;//從頂部滑動進入
kCATransitionReveal;//把原始圖層推出來來顯示新圖層
//subtype也有四個方法,主要來控制type的方向
kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
有一點特別的是披泪,例子中的CATransition只用于更換圖片橘蜜。另外在無論有沒有設(shè)置key,它的key都是“transition”。其實计福,在系統(tǒng)中跌捆,設(shè)置layer的contens屬性時,CATransition是默認的行為象颖,也就是說佩厚,系統(tǒng)加上了隱式過渡行為。對于UIView说订,這個特性也像其他隱式動畫一樣被禁用抄瓦。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
CATransition *transition = [CATransition animation];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromBottom;
transition.duration = 2;
[self.colorLayer addAnimation:transition forKey:nil];
UIImage *image = [UIImage imageNamed:@"person"];
self.colorLayer.contents = (__bridge id)image.CGImage;
}
CATransition除了會對不可動畫屬性做過渡動畫外,也可以對圖層樹做動畫陶冷。比如給UITabBarController切換標簽時加入淡入淡出的效果钙姊。
#import "AppDelegate.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launch {
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
UIViewController *viewController1 = [[FirstViewController alloc] init];
UIViewController *viewController2 = [[SecondViewController alloc] init];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = @[viewController1, viewController2];
self.tabBarController.delegate = self;
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewContro {
//set up crossfade transition
CATransition *transition = [CATransition animation];
transition.type = kCATransitionFade;
//apply transition to tab bar controller's view
[self.tabBarController.view.layer addAnimation:transition forKey:nil];
}
@end
UIKit也有提供對應(yīng)的方法來做過渡動畫。 我們可以通過UIView的+transitionFromView:toView:duration:options:completion:和*+transitionWithView:duration:opt ions:animations: *來實現(xiàn)埂伦。
[UIView transitionWithView:self.view duration:2 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
UIImage *image = [UIImage imageNamed:@"person"];
self.view.layer.contents = (__bridge id)image.CGImage;
} completion:nil];
通過上面對CAAnimation子類的學習煞额,我們了解了各個子類的特點和應(yīng)用范圍。以下我們再介紹幾個它們共用的特性沾谜,也就是CAAnimation的某些特點:
Key的用途(標識符膊毁,移除圖層)
當使用addAnimation:forKey:把動畫添加到圖層時,這個key是為了給動畫取個標識符基跑,也可以用animationKeys獲取圖層的所有動畫標識符婚温。
我們可以用animationForKey:來獲取key對應(yīng)的動畫,因為不能在動畫執(zhí)行過程中修改動畫媳否,所以這個方法主要用來檢測動畫的屬性栅螟,判斷它是否被添加到圖層中。
另外篱竭,為了終止動畫嵌巷,可以把它從圖層中移除:
//通過key移除某個動畫
- (void)removeAnimationForKey:(NSString *)key;
//移除該圖層所有的動畫
- (void)removeAllAnimations;
動畫一旦被移除,圖層的外觀就立刻更新到當前的模型圖層的值室抽。一般來說搪哪,動畫結(jié)束后會被自動移除,除非設(shè)置removedOnCompletion的值為NO坪圾。當你設(shè)置動畫在結(jié)束后不被自動移除晓折,那么當它不需要時你要手動移除,否則它會存在內(nèi)存中兽泄,直到圖層被銷毀漓概。
CAMediaTiming協(xié)議
CAMediaTiming協(xié)議定義了在一段動畫內(nèi)用來控制逝去時間的屬性的集合,CALayer和CAAnimation都實現(xiàn)了這個協(xié)議病梢,所以可以通過這兩個類來控制時間胃珍。
其中CAMediaTiming協(xié)議有這么幾個常用的屬性:
- duration
動畫時間梁肿。(默認值是0,不代表是0秒觅彰,而是代表著0.25秒) - repeatCount
重復(fù)次數(shù)吩蔑。(默認值是0,不代表是0次填抬,而是代表著1次) - repeatDuration
重復(fù)一個指定的時間烛芬。(比如duration為2,repeatDuration為10飒责,代表執(zhí)行總時間10s赘娄,重復(fù)5次) - autoreverses
在每次間隔交替循環(huán)過程中自動回放。(比如開門的動畫宏蛉,設(shè)置autoreverses為YES遣臼,門打開后會自動關(guān)閉)
以下屬性主要用于相對時間:
- beginTime
動畫開始之前的延遲時間。(這里的延遲從動畫添加到可見圖層的那一刻開始拾并,默認是0揍堰,就是立刻開始執(zhí)行) - timeOffset
讓動畫快進到某個時間點。(比如一個duration為1的動畫辟灰,timeOffset為0.5,意思是從一半的地方開始執(zhí)行篡石,跳過前面那段的動畫) - speed
speed是一個執(zhí)行速度芥喇,默認是1。(比如一個duration為1的動畫凰萨,speed為2继控,實際上動畫0.5秒就可以完成,假如timeOffset為0.5胖眷,意味著動畫從結(jié)束的位置開始武通,也就是說timeOffset其實不受speed影響,而duration會) - fillMode
kCAFillModeRemoved;//默認值珊搀,回到原始狀態(tài)
kCAFillModeForwards;//保留動畫結(jié)束后的狀態(tài)
kCAFillModeBackwards;
kCAFillModeBoth;
實現(xiàn)一個手勢控制開門的動畫來了解各個屬性:
- (void)createDoorAnimation{
//門
self.containView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 128, 256)];
self.containView.backgroundColor = [UIColor grayColor];
[self.view addSubview:self.containView];
//門板
self.doorLayer = [CALayer layer];
self.doorLayer.frame = CGRectMake(0, 0, 128, 256);
self.doorLayer.position = CGPointMake(0, 128);
self.doorLayer.anchorPoint = CGPointMake(0, 0.5);
UIImage *image = [UIImage imageNamed:@"person"];
self.doorLayer.contents = (__bridge id)image.CGImage;
[self.containView.layer addSublayer:self.doorLayer];
//設(shè)置3d效果
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0/500;
self.containView.layer.sublayerTransform = transform;
//添加手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
[pan addTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
//設(shè)置為0就沒有動畫冶忱,手動控制timeOffset來實現(xiàn)關(guān)門動畫
self.doorLayer.speed = 0;
self.doorLayer.autoreverses = NO;
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation.y";
animation.toValue = @(-M_PI_2);
animation.duration = 2.0;
[self.doorLayer addAnimation:animation forKey:nil];
}
- (void)pan:(UIPanGestureRecognizer *)pan{
CGFloat x = [pan translationInView:self.view].x;
x /= 200.0f;
CFTimeInterval timeOffset = self.doorLayer.timeOffset;
timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
self.doorLayer.timeOffset = timeOffset;
[pan setTranslation:CGPointZero inView:self.view];
}
緩沖函數(shù)
timingFunction是控制動畫的緩沖函數(shù),是CAMediaTimingFunction類的對象境析,調(diào)用+timingFunctionWithName:構(gòu)造方法傳入如下幾個常量之一:
kCAMediaTimingFunctionLinear(線性):勻速囚枪,給你一個相對靜態(tài)的感覺
kCAMediaTimingFunctionEaseIn(漸進):動畫緩慢進入,然后加速離開
kCAMediaTimingFunctionEaseOut(漸出):動畫全速進入劳淆,然后減速的到達目的地
kCAMediaTimingFunctionEaseInEaseOut(漸進漸出):動畫緩慢的進入链沼,中間加速,然后減速的到達目的地沛鸵。這個是默認的動畫行為括勺。
同樣的,針對UIView也支持緩沖方法,盡管語法和常量不一樣疾捍。
UIViewAnimationOptionCurveEaseInOut
UIViewAnimationOptionCurveEaseIn
UIViewAnimationOptionCurveEaseOut
UIViewAnimationOptionCurveLinear
//應(yīng)用函數(shù)
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
//動畫內(nèi)容
} completion:^(BOOL finished) {
//完成
}];
另外奈辰,CAKeyframeAnimation有個timingFunctions屬性,是用來存放緩沖函數(shù)的數(shù)組拾氓,可以用它來對每次動畫的步驟指定不同的計時函數(shù)冯挎。當然,我們也可以自定義緩沖函數(shù)咙鞍,不過這個相對復(fù)雜挺多房官。
基于動畫的定時器
動畫能做到的僅僅是足夠快地展示一系列靜態(tài)圖片,只是看起來像運動续滋。iOS按照每秒60次刷新屏幕翰守,然后CAAnimation計算出需要展示的新的幀,然后在屏幕更新的時候同步繪制上去疲酌。
在介紹基于動畫的定時器前蜡峰,我們先來熟悉下之前經(jīng)常接觸的定時器NSTimer,我們先來了解下NSTimer的工作原理朗恳。iOS每個線程上都管理著一個RunLoop湿颅,就是通過這個循環(huán)來完成一些任務(wù)列表,比如主線程的任務(wù)列表就有以下任務(wù):
- 處理觸摸事件
- 執(zhí)行使用GCD的代碼
- 處理計時器行為
- 屏幕重繪
當你設(shè)置一個NStimer粥诫,它會被插入當前的任務(wù)列表中油航,然后直到指定時間過去后才會被執(zhí)行。當時它只會在列表中的上一個任務(wù)完成后才開始執(zhí)行怀浆,這樣有時候會導致延遲谊囚。(根據(jù)上一個任務(wù)的情況,少幾毫秒执赡,多則延遲很長時間)镰踏。
CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時器類。跟NSTimer以秒為單位不同沙合,CADisplayLink有一個整型的frameInterval屬性奠伪,指定了間隔多少幀之后開始執(zhí)行。
iOS設(shè)備的屏幕刷新頻率是固定的首懈,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用芳来,精確度相當高。當然使用CADisplayLink也不能保證每一幀都按照計劃執(zhí)行猜拾,一些任務(wù)可能會導致動畫偶爾丟幀即舌。
CADisplayLink使用場合相對專一,適合做UI的不停重繪挎袜,比如自定義動畫引擎或者視頻播放的渲染顽聂。NSTimer的使用范圍要廣泛的多肥惭,各種需要單次或者循環(huán)定時處理的任務(wù)都可以使用。在UI相關(guān)的動畫或者顯示內(nèi)容使用 CADisplayLink比起用NSTimer的好處就是我們不需要在格外關(guān)心屏幕的刷新頻率了紊搪,因為它本身就是跟屏幕刷新同步的蜜葱。
//CADisplayLink
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step:)];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
//NSTimer
self.timer = [NSTimer timerWithTimeInterval:1/60.0
target:self
selector:@selector(step:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer
forMode:NSRunLoopCommonModes];