iOS 動畫篇 - UIKit動畫(一)
在前面文章中,有詳細介紹過 iOS-%E5%8A%A8%E7%94%BB%E7%AF%87-Core-Animation/" target="_blank" rel="nofollow,noindex">Core Animation 動畫 ,UIKit 動畫實質上是針對核心動畫的封裝,不同的是,核心動畫操作的是圖層級別(CALayer),通常情況下,它不會影響到檢視層,而 UIKit 針對的是檢視級別(UIView),我們在做此動畫後,檢視的 frame、center等屬性都會響應的變化。
在入場開發中,UIKit 動畫塊可以完成百分之九十以上的動畫任務,這些動畫任務通常是針對檢視的可動畫屬性,例如改變檢視的frame、center、bounds、backgroundColor、alpha、transform等等。在某些特殊的情況下(如改變檢視的陰影、圓角等),UIKit 完成不了的時候,你依舊可以藉助核心動畫來完成。
由於 UIKit 動畫是核心動畫的一層封裝,使用起來相比於核心動畫要簡單很多,關於一些原理的知識,你可以檢視 Core Animation 動畫 ,此篇文章只做 UIKit 動畫的使用介紹。
UIView 動畫
UIView 動畫是對 UIView 的分類擴充套件。你可以使用檢視的類方法對任意的檢視類做屬性動畫而無需 Core Animation 那種建立動畫例項。
begin/commit
在做動畫時候,你需要將檢視的可動畫屬性的新值放置在以後兩個方法中:
+beginAnimations:context:
該方法是動畫設定的開始起點,兩個引數分別是動畫標誌符和額外引數
+commitAnimations
該方法是動畫設定的介紹終點,呼叫該方法,表示提交當前動畫的所有設定,動畫入棧,等待 runloop 的執行。
// 開始設定動畫 [UIView beginAnimations:@"animations" context:nil]; // 可動畫屬性設定預期的新值 self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; // 提交動畫 [UIView commitAnimations];

設定動畫
在上述例子中,我們改變了檢視的中心點位置以及填充色。執行我們發現,檢視達到我們預期所設定的新值。但是我們發現,這過程太過短暫,因為上述例子中使用了系統預設的動畫時間,即0.2秒。接下來我們通過方法 +setAnimationDuration:
來改變動畫的時間。
[UIView beginAnimations:@"animations" context:nil]; // 設定動畫時間 [UIView setAnimationDuration:1.0]; self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; [UIView commitAnimations];

設定動畫時間
此時由於動畫時長被設定為1秒,使得動畫能夠平滑動轉變為新值。
需要注意的是:設定動畫的時長需要放置在屬性設定之前,否則無效。動畫其他設定大部分都是如此。
類似的,我們還可以使用其他方法來設定我們的動畫,其中有:
+setAnimationDuration: //動畫時長,預設為0.2 +setAnimationDelay: //延遲動畫,預設為0.0 +setAnimationStartDate: //指定日期開始動畫,預設為當前時間 +setAnimationCurve: //動畫的時間速率,預設為UIViewAnimationCurveEaseInOut +setAnimationRepeatCount: //重複次數,預設為0 +setAnimationRepeatAutoreverses: //是否執行反轉動畫 +setAnimationsEnabled: //動畫執行能力,可關閉動畫
動畫過程
除此之外,如果我們想要監聽動畫的執行過程事件,該怎麼辦呢?UIKit 動畫為我們提供了三個方法,別分用來設定動畫的代理監聽物件以及動畫的開始和結束事件。
+setAnimationDelegate: //設定動畫代理 +setAnimationWillStartSelector: //動畫開始事件 +setAnimationDidStopSelector: //動畫結束事件
示例:我們為動畫新增代理,實現動畫的結束代理方法
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [UIView beginAnimations:@"animations" context:nil]; [UIView setAnimationDuration:1.0]; self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; // 設定動畫代理 [UIView setAnimationDelegate:self]; // 設定動畫開始和結束代理 [UIView setAnimationWillStartSelector:@selector(start:)]; [UIView setAnimationDidStopSelector:@selector(end:)]; [UIView commitAnimations]; } -(void)start:(NSString*)flag{ //NSString* tip = [NSString stringWithFormat:@"動畫開始-%@",flag]; //[[[UIAlertView alloc] initWithTitle:@"提示" message:tip delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil] show]; } -(void)end:(NSString*)flag{ NSString* tip = [NSString stringWithFormat:@"動畫結束-%@",flag]; [[[UIAlertView alloc] initWithTitle:@"提示" message:tip delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil] show]; }

動畫結束事件
和其他方法不同,設定動畫代理的三個方法只要 begin/commit
之間即可。
轉場動畫
UIKit 動畫中還有一個轉場動畫,該類通常需要兩個或多個檢視。
+setAnimationTransition:forView:cache:
引數 transition:是轉場型別,有以下幾個:
typedef enum UIViewAnimationTransition : NSInteger { UIViewAnimationTransitionNone, UIViewAnimationTransitionFlipFromLeft, UIViewAnimationTransitionFlipFromRight, UIViewAnimationTransitionCurlUp, UIViewAnimationTransitionCurlDown } UIViewAnimationTransition;
引數 forView:轉場動畫所應用到的檢視
引數 cache:快取可以提高效能,但如果將此引數設定為YES,則不得在轉換期間更新檢視或其子檢視,因為更新檢視及其子檢視可能會干擾快取行為,並導致檢視內容在動畫期間被錯誤地(或在錯誤的位置)呈現,必須等到轉換結束才能更新檢視。如果為“否”,則必須為過渡動畫的每個幀更新檢視及其內容,這可能會顯著影響幀速率。
如果你想從一個檢視翻轉到另一個檢視,以下為系統提供的步驟:
-
Begin an animation block.
-
Set the transition on the container view.
-
Remove the subview from the container view.
-
Add the new subview to the container view.
-
Commit the animation block.
無非就是對容器檢視子檢視的移除和新增操作。我們這裡例子只有兩個檢視,因此採用另外方式:做透明度的設定,在翻轉的時候隱藏其中一個。
[UIView beginAnimations:@"animations" context:nil]; [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.blueView cache:NO]; self.redView.alpha = !self.redView.alpha; [UIView setAnimationDuration:1.0]; [UIView commitAnimations];

轉場動畫
下面演示了不同的轉場動畫效果:

四種不同型別的轉場動畫
上述就是對 UIView 的簡單介紹和使用了。UIView 動畫通常只能夠對檢視屬性的可動畫屬性有效,如果我們想實現例如關鍵幀,彈性動畫等,似乎很困難。另外如果我們想要在一個動畫結束之後,緊接著開始另一段動畫(甚至更多的動畫),我們需在代理方法中加上動畫標誌符加以區分,或者需要更多的方法選擇器指向不同方法,無論是哪一種方式,似乎都加深了程式碼的複雜度。
出於上述的問題考慮,蘋果在 iOS4.0 之後推出了 UIView 動畫塊,並且強烈推薦開發者使用新的 UIView 動畫塊來實現檢視動畫。新的動畫方式對之前的動畫再次進行了封裝,讓我們做動畫時不在需要進行 begin/commit
以及設定動畫屬性時呼叫大量的程式碼,而採用了諸多的快捷建立方式。另外將動畫的完成事件通過block塊傳遞,從而可以一對一的形式傳遞動畫過程。使用動畫塊的另外一個好處就是我們可在完成塊中繼續使用新的動畫塊,從而執行新的動畫。
UIView 動畫塊不僅提供屬性動畫,對應 Core Animation 中的 CAKeyframeAnimation 關鍵幀動畫、CASpringAnimation 彈性動畫等都提供了相應的方法,這讓我們實現的動畫效果變得更多,使用更簡便了。
UIView 動畫塊
屬性動畫
UIView 動畫塊依舊是檢視的類方法,如果你想要簡潔的做一些檢視屬性動畫,可以使用下面的方法。
+animateWithDuration:animations:
在此方法中,你可以設定動畫的時間和需要執行的屬性動畫。
示例:
[UIView animateWithDuration:1.0 animations:^{ self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; }];

設定屬性動畫
但是我們注意到,此方法中並沒有動畫完成的回撥,如果我需要在一個動畫結束之後做一些其他的事情,如執行另外一個動畫。此時你需要使用下面的方法:
+animateWithDuration:animations:completion:
該方法相比於前一個方法,多個一個完成塊,該完成塊會在動畫完成後回撥。
[UIView animateWithDuration:1.0 animations:^{ self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; } completion:^(BOOL finished) { // 執行另外一個動畫 [UIView animateWithDuration:1.0 animations:^{ CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI); transform = CGAffineTransformScale(transform, 0.65, 1.0); self.blueView.transform = transform; }]; }];

帶完成塊的動畫
但是,該方法似乎還是不可以滿足我們的動畫需求,因此次方法無法設定動畫延遲、動畫速率等。實際上,蘋果提供了一個動畫引數相對較多的動畫方法,上述的兩個方法只不過為了滿足一些簡單的屬性動畫而在該方法中封裝了一層而已。
+animateWithDuration:delay:options:animations:completion:
該方法提供了動畫時長、延遲動畫時間、動畫塊、完成塊等,其中還有一個可選引數 options,該引數是 UIViewAnimationOptions 型別,是一個列舉型別,引數分為三類,可以組合使用:
// 1.常規動畫屬性設定(可以同時選擇多個進行設定) UIViewAnimationOptionLayoutSubviews:動畫過程中保證子檢視跟隨運動。 UIViewAnimationOptionAllowUserInteraction:動畫過程中允許使用者互動。 UIViewAnimationOptionBeginFromCurrentState:所有檢視從當前狀態開始執行。 UIViewAnimationOptionRepeat:無限重複執行動畫。 UIViewAnimationOptionAutoreverse :動畫執行到結束點後仍然以動畫方式回到初始點。 UIViewAnimationOptionOverrideInheritedDuration:忽略巢狀動畫時間設定。 UIViewAnimationOptionOverrideInheritedCurve:忽略巢狀動畫速度設定。 UIViewAnimationOptionAllowAnimatedContent:動畫過程中重繪檢視(注意僅僅適用於轉場動畫)。 UIViewAnimationOptionShowHideTransitionViews:檢視切換時直接隱藏舊檢視、顯示新檢視,而不是將舊檢視從父檢視移除(僅僅適用於轉場動畫) UIViewAnimationOptionOverrideInheritedOptions :不繼承父動畫設定或動畫型別。 // 2.動畫速度控制(可從其中選擇一個設定) UIViewAnimationOptionCurveEaseInOut:動畫先緩慢,然後逐漸加速。 UIViewAnimationOptionCurveEaseIn :動畫逐漸變慢。 UIViewAnimationOptionCurveEaseOut:動畫逐漸加速。 UIViewAnimationOptionCurveLinear :動畫勻速執行,預設值。 // 3.轉場型別(僅適用於轉場動畫設定,可以從中選擇一個進行設定,基本動畫、關鍵幀動畫不需要設定) UIViewAnimationOptionTransitionNone:沒有轉場動畫效果。 UIViewAnimationOptionTransitionFlipFromLeft :從左側翻轉效果。 UIViewAnimationOptionTransitionFlipFromRight:從右側翻轉效果。 UIViewAnimationOptionTransitionCurlUp:向後翻頁的動畫過渡效果。 UIViewAnimationOptionTransitionCurlDown :向前翻頁的動畫過渡效果。 UIViewAnimationOptionTransitionCrossDissolve:舊檢視溶解消失顯示下一個新檢視的效果。 UIViewAnimationOptionTransitionFlipFromTop :從上方翻轉效果。 UIViewAnimationOptionTransitionFlipFromBottom:從底部翻轉效果。
示例:我們使用上述方法來執行動畫的時間速率和重複行為。
[UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveEaseIn animations:^{ self.blueView.center = self.view.center; self.blueView.backgroundColor = UIColor.redColor; } completion:^(BOOL finished) { // do something }];

設定多選項動畫
在引數選項中,我們組合使用了 UIViewAnimationOptionRepeat 和 UIViewAnimationOptionCurveEaseIn,即重複和緩慢進入的動畫效果。
一般來說,前兩種方法的組合使用就可以完成大部分的屬性動畫,第三種用到的時候相對較少。
轉場動畫
在 iOS4.0 時期,除了上述的的屬性動畫,還有針對 +setAnimationTransition:forView:cache:
轉場動畫的封裝。
+transitionWithView:duration:options:animations:completion:
示例:
[UIView transitionWithView:self.blueView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ self.redView.alpha = !self.redView.alpha; } completion:nil];
該方法需要指定轉場動畫的檢視 view,需要animations動畫塊中執行轉場動畫,通常是對內容檢視containerView子檢視的移除和新增,就像下面的示例:
官方示例:
[UIView transitionWithView:containerView duration:0.2 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ [fromView removeFromSuperview]; [containerView addSubview:toView]; } completion:NULL];
在前一節的 UIView 動畫中,系統提供了四種轉場動畫,而在 iOS4.0 的動畫塊中,系統提供了另外三種轉場動畫:

七種轉場動畫
除此方法,系統還提供了另外一種轉場動畫方法,此方法關注點是 fromView
和 toView
,即從某個檢視轉場到某個檢視,其中應用轉場動畫檢視為 fromView
的父檢視。
示例1:
@interface ViewController () @property(strong, nonatomic)UIView* baseView; @property(strong, nonatomic)UIView* fromView; @property(strong, nonatomic)UIView* toView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)]; [self.view addSubview:self.baseView]; self.baseView.center = self.view.center; self.fromView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)]; [self.baseView addSubview:self.fromView]; self.fromView.backgroundColor = [UIColor redColor]; self.toView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)]; self.toView.backgroundColor = [UIColor yellowColor]; } -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [UIView transitionFromView:self.fromView toView:self.toView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) { }]; }

另一種轉場動畫
示例2:切換導航控制器
通常我們切換導航控制器時,如果直接設定應用的根控制器的話,是沒有任何動畫效果的,刷的一下很突然,因此我們通過使用此方法來演示轉場動畫的使用。
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // 建立新的導航控制器 NextPageViewController* next = [NextPageViewController new]; UINavigationController* navNew = [[UINavigationControlleralloc] initWithRootViewController:next]; // 獲取當前的導航控制器 UITabBarController* tabCtrl = (UITabBarController*)[[UIApplication sharedApplication] keyWindow].rootViewController; // 設定轉場動畫 [UIView transitionFromView:tabCtrl.view toView:navNew.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) { // 將新的導航控制器設定為 keyWindow 的根控制器 UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow]; keyWindow.rootViewController = navNew; }]; }

使用過場動畫切換導航控制器
以上就是 iOS4.0 推出的 UIView 動畫塊,主要是針對 iOS2.0 中 UIView 動畫的封裝。
在 iOS7.0,蘋果又推出了彈性動畫和關鍵幀動畫,以及消除隱式動畫的方法。
彈性動畫
彈性動畫依舊是沿用了檢視的類方法的呼叫形式,在 Core Animaiton 中與之相對應的是 CASpringAnimation,不過 CASpringAnimation 是在 iOS9.0 後才推出來,稍晚於 UIView 的動畫塊。
+animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
該方法中出現了兩個新的引數名:
引數 dampingRatio 表示阻尼比,取值在(0,1),期值越小振盪的幅度和次數會增加,反之則減少。
引數 velocity 表示檢視的初始速度。對於這個值的解釋,官方文件的說明與實際效果並不一致,不過速度越大,振幅越大。
在使用彈性動畫過程中,建議調整動畫時間(影響執行速度)和阻尼比。
示例:
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.3 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{ self.blueView.center = self.view.center; } completion:nil];

彈性動畫
關鍵幀動畫
和之前的的幾種動畫不同,關鍵幀動畫關注的不僅僅是開始和結束的位置,關鍵幀動畫可設定的位置有多個,因此在設定關鍵幀動畫時,一個方法必然是非常困難的。蘋果將關鍵幀動畫拆成了兩個,一個用來設定動畫的時長、執行模式等,另外一個用來設定檢視在每一幀的新值。
關鍵幀動畫的基本設定:
+animateKeyframesWithDuration:delay:options:animations:completion:
該方法用來設定關鍵幀動畫的基本情況,其中包括動畫執行的總時長、延遲時間、執行模式、動畫塊、完成塊。
在動畫塊中,你可以設定關鍵幀的動畫情況。當然你也可以在該動畫塊中設定新值,就像下面這樣:
[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{ self.blueView.backgroundColor = UIColor.redColor; self.blueView.center = self.view.center; self.blueView.transform = CGAffineTransformMakeScale(0.6, 1.0); } completion:nil];
這表示你的關鍵幀只有開始(初始狀態)和結束,但是這和之前的屬性動畫並無二致: +animateWithDuration:animations:
,那又何必使用關鍵幀動畫呢?

關鍵幀動畫示例
如果只關注檢視的開始和結束狀態,那麼使用關鍵幀動畫似乎顯得有點大材小用,關鍵幀的作用是可以讓你設定多個位置的狀態的,並且無需動畫巢狀(屬性動畫在完成後繼續巢狀新動畫),而是將整個過程作為一個完成的動畫。
新增關鍵幀:
+addKeyframeWithRelativeStartTime:relativeDuration:animations:
引數 frameStartTime 和 frameDuration 都是相對的,相對整個動畫時間。
frameStartTime:指當前幀的啟動時間,如0表示動畫的開始位置,0.5表示動畫執行一半的時刻。
frameDuration:指動畫到指定值的時間長度,如0.5表示相對動畫總時長的一半時間。
示例:設定檢視的多個位置的狀態
[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{ // 動畫的 0 - 0.25 的部分 [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.25 animations:^{ self.blueView.center = CGPointMake(self.blueView.center.x+200, self.blueView.center.y); self.blueView.transform = CGAffineTransformMakeScale(0.6, 1.0); }]; // 動畫的 0.25 - 0.5 的部分 [UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{ self.blueView.center = CGPointMake(self.blueView.center.x, self.blueView.center.y-200); self.blueView.transform = CGAffineTransformIdentity; }]; // 動畫的 0.5 - 0.75 的部分 [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{ self.blueView.center = CGPointMake(self.blueView.center.x-200, self.blueView.center.y); self.blueView.transform = CGAffineTransformMakeRotation(M_PI_2); }]; // 動畫的 0.75 - 1 的部分 [UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{ self.blueView.center = CGPointMake(self.blueView.center.x, self.blueView.center.y+200); self.blueView.transform = CGAffineTransformIdentity; }]; // 動畫的 0 - 1 的部分 [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:1.0 animations:^{ self.blueView.backgroundColor = UIColor.redColor; }]; } completion:nil];

image
這裡有一個引數 options,是UIViewKeyframeAnimationOptions 型別,和上面屬性動畫引數設定有些差別,關鍵幀動畫設定引數分為兩類,也可以組合使用:
// 1.常規動畫屬性設定(可以同時選擇多個進行設定) UIViewAnimationOptionLayoutSubviews:動畫過程中保證子檢視跟隨運動。 UIViewAnimationOptionAllowUserInteraction:動畫過程中允許使用者互動。 UIViewAnimationOptionBeginFromCurrentState:所有檢視從當前狀態開始執行。 UIViewAnimationOptionRepeat:重複執行動畫。 UIViewAnimationOptionAutoreverse :動畫執行到結束點後仍然以動畫方式回到初始點。 UIViewAnimationOptionOverrideInheritedDuration:忽略巢狀動畫時間設定。 UIViewAnimationOptionOverrideInheritedOptions :不繼承父動畫設定或動畫型別。 // 2.動畫模式設定(同前面關鍵幀動畫動畫模式一一對應,可以從其中選擇一個進行設定) UIViewKeyframeAnimationOptionCalculationModeLinear:連續運算模式。 UIViewKeyframeAnimationOptionCalculationModeDiscrete :離散運算模式。 UIViewKeyframeAnimationOptionCalculationModePaced:均勻執行運算模式。 UIViewKeyframeAnimationOptionCalculationModeCubic:平滑運算模式。 UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均勻運算模式。
看過之前的 Core Animation 的小夥伴肯定還知道 CAKeyframeAnimation 關鍵幀動畫,還可以將關鍵幀無限增加從而演變為路徑關鍵幀動畫,然而關於這一點,目前 UIView 動畫塊還不支援,所以設計到路徑的動畫還是依賴 Core Animation 核心動畫來實現。