Core Animation(中)─── 動畫

前面我們介紹了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對象.png

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];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耀石,隨后出現(xiàn)的幾起案子牵囤,更是在濱河造成了極大的恐慌,老刑警劉巖滞伟,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揭鳞,死亡現(xiàn)場離奇詭異,居然都是意外死亡梆奈,警方通過查閱死者的電腦和手機野崇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亩钟,“玉大人乓梨,你說我怎么就攤上這事∏逅郑” “怎么了扶镀?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長焰轻。 經(jīng)常有香客問我臭觉,道長,這世上最難降的妖魔是什么鹦马? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任胧谈,我火速辦了婚禮忆肾,結(jié)果婚禮上荸频,老公的妹妹穿的比我還像新娘。我一直安慰自己客冈,他們只是感情好旭从,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著场仲,像睡著了一般和悦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渠缕,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天鸽素,我揣著相機與錄音,去河邊找鬼亦鳞。 笑死馍忽,一個胖子當著我的面吹牛棒坏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遭笋,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坝冕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瓦呼?” 一聲冷哼從身側(cè)響起喂窟,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎央串,沒想到半個月后磨澡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蹋辅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年钱贯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侦另。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秩命,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出褒傅,到底是詐尸還是另有隱情弃锐,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布殿托,位于F島的核電站霹菊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏支竹。R本人自食惡果不足惜旋廷,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望礼搁。 院中可真熱鬧饶碘,春花似錦、人聲如沸馒吴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饮戳。三九已至豪治,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扯罐,已是汗流浹背负拟。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留歹河,地道東北人掩浙。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓琉挖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涣脚。 傳聞我的和親對象是個殘疾皇子示辈,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353