iOS UIView層動畫

weixin_33860722發表於2017-05-19

前言:

UIVIew Animation 是 iOS 提供的最基礎的一組用於實現 UIView 動畫的類庫。在 UIView Animation 中,可以改變的屬性有:
frame
bounds
center
alpha
transform

一: 可實現動畫的屬性

現在你已經可以製作簡單的動畫了,但要記住:不是所有修改屬性的操作放到animations程式碼塊中都是變成動畫實現的 —— 不管你怎麼修改一個檢視的tag,或者是delegate。因此,可實現動畫的屬性必定會導致檢視的重新渲染。這些可以生成動畫的屬性大致可以分成這麼三類:座標尺寸、檢視顯示、形態變化。

  • 座標尺寸類
    bounds:修改這個屬性會結合center屬性重新計算frame。建議通過這個屬性修改尺寸。
    frame:修改這個屬性通常會導致檢視形變的同時也發生移動,然後會重新設定center跟bounds屬性
    center: 設定後檢視會移動到一個新位置,修改後會結合bounds重新計算frame。
1400111-afc1840ec57ba949.png
尺寸修改
  • 檢視顯示類
    backgroundColor: 修改這個屬性會產生顏色漸變過渡的效果,本質上是系統不斷修改了tintColor來實現的。
    alpha:修改這個屬性會產生淡入淡出的效果。
    hidden:修改這個屬性可以製作翻頁隱藏的效果。
1400111-290fd2ca98f9e918.png
修改透明度
  • 形態變化類
    transform:修改這個屬性可以實現旋轉、形變、移動、翻轉等動畫效果,其通過矩陣運算的方式來實現,因此更加強大。
1400111-83bb0912c36df872.png
旋轉

二: 動畫種類

UIView 類提供了大量的動畫 API ,這些 API 集中在三個 Category 裡,分別是:
UIView (UIViewAnimation) - basic animation 基礎動畫
UIView (UIViewAnimationWithBlocks) - basic animation 基礎動畫
UIView (UIViewKeyframeAnimations) - keyframe animation 關鍵幀動畫

1. UIViewAnimation

UIViewAnimation 誕生時間最早,功能上完全可以使用其餘兩個 Cagetory 替代,其中方法包含:

+ (void)beginAnimations:(nullable NSString )animationID context:(nullable void )context; 
+ (void)commitAnimations; + (void)setAnimationDelegate:(nullable id)delegate; + (void)setAnimationWillStartSelector:(nullable SEL)selector;
+ (void)setAnimationDidStopSelector:(nullable SEL)selector; 
+ (void)setAnimationDuration:(NSTimeInterval)duration;
+ (void)setAnimationDelay:(NSTimeInterval)delay; 
+ (void)setAnimationStartDate:(NSDate )startDate; 
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
+ (void)setAnimationRepeatCount:( float)repeatCount;
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses; 
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView )view cache:(BOOL)cache; 
+ (void)setAnimationsEnabled:(BOOL)enabled; 
+ (BOOL)areAnimationsEnabled; //阻塞動畫,iOS 7 新增的新方法[UIView performWithoutAnimation:]。它是一個簡單的封裝,先檢查動畫當前是否啟用,然後禁止動畫,執行塊語句,最後重新啟用動畫。需要說明的地方是,它並不會阻塞基於CoreAnimation的動畫。
 + (void)performWithoutAnimation:(void (^)(void))actionsWithoutAnimation + (NSTimeInterval)inheritedAnimationDuration

//方法比較簡單,使用時先 beginAnimations(傳入的 animationID 作為該動畫的標識,可以在 delegate 中清楚的識別到該動畫, 然後設定動畫的各項屬性,如 duration, delegate等,設定完成後 **commitAnimations。
- (void) startAnimation { 
[UIView beginAnimations:@"UIViewAnimation" context:(__bridge void *)(self)]; [UIView setAnimationDuration:1.0]; [UIView setAnimationDelay:0.0];
 [UIView setAnimationRepeatCount:2]; 
[UIView setAnimationRepeatAutoreverses:YES]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDelegate:self]; 
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; _animationView.center = CGPointMake(CGRectGetMaxX(self.view.bounds) - 25, CGRectGetMidY(self.view.bounds));
 [UIView commitAnimations];
}

需要說明的是,UIViewAnimationCurve 表示動畫的變化規律:


UIViewAnimationCurveEaseInOut: 開始和結束時較慢
UIViewAnimationCurveEase: 開始時較慢
UIViewAnimationCurveEaseOut: 結束時較慢
UIViewAnimationCurveLinear: 整個過程勻速進行


具體效果可參考下圖:


1400111-66d77344d4abff2b.gif
基礎動畫

2. UIViewAnimationWithBlocks

UIViewAnimationWithBlocks 是在 iOS 4.0 時推出的基於 block 的 Animation Category,較 UIViewAnimation 來說,使用起來更加便捷。

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations ; 
+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray<kindof UIView > )views options:(UIViewAnimationOptions)options animations:(void (^ nullable)(void))parallelAnimations completion:(void (^ nullable)(BOOL finished))completion ;

這是 UIViewAnimationWithBlocks 中最常用的三種方法,使用 block 方式實現基本動畫。


可以設定 duration
持續時間,delay
延遲時間,UIViewAnimationOptions
列舉項和completion
動畫結束的回撥。
時間函式
動畫的速度曲線是由時間函式( timing function )控制的。


  • 簡單動畫程式碼示例

先放上本文demo:點這

在使用者開啟app要進行登入的時候,賬戶和密碼輸入框從螢幕的左邊進入,接著登入按鈕出現。


1400111-3730c82bbf0d0204.gif

介面動畫

在這段動畫之中發生的最為明顯的事情就是兩個文字框的位置變化,在動畫開始之前,兩個文字框的位置應該是在螢幕的左邊,而下方的按鈕現在是隱藏狀態(設定alpha)


1400111-c30b7b03d5e655ce.png

動畫開始前

因此,這個動畫之中發生的事情,我們可以用概括為下面的程式碼:

self.userName.center.x += offset; //userName進入
self.password.center.x += offset; //password進入
self.login.alpha = 1; //顯示登入按鈕

既然已經知道了我們的動畫發生了什麼,接著就可以使用UIKit
的動畫API讓我們的動畫活起來了

//設定文字框初始位置為螢幕左側
CGPoint accountCenter = self.userName.center;
CGPoint psdCenter = self.password.center;
accountCenter.x -= 200;
pasCenter.x -= 200;
self.userName.center = accountCenter;
self.password.center = psdCenter;//還原中心座標
accountCenter.x += 200;
psdCenter.x += 200; 
[UIView animateWithDuration: 0.5 animations: ^{ self.userName.center = accountCenter; self.password.center = passwordCenter; self.login.alpha = 1;} completion: nil];

在UIKit中,系統提供了animate標題打頭的屬於UIView的類方法讓我們可以輕鬆的製作動畫效果,每一個這樣的類方法提供了名為animations的block
程式碼塊,這些程式碼會在方法呼叫後立刻或者延遲一段時間以動畫的方式執行。此外,所有這些API的第一個引數都是用來設定動畫時長的。
在viewDidAppear:中執行這段程式碼,你會看到文字框從左側滑動,按鈕也漸變顯示出來的,但是跟我們要的結果不太一樣 —— 三個動畫沒有錯開,效果並不那麼的好看。我們希望密碼框能在賬戶文字框滑動後的一段時間後再出現,按鈕同樣也需要晚一些顯示。所以,我們需要使用下面的方法來實現這個效果:

  • 更新約束
  leftContrain.constant = 100
 UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
                self.view.layoutIfNeeded() //立即實現佈局
            }, completion: nil)
  • Repeating
 [UIView animateWithDuration: 0.5 delay: 0.35 options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations: ^{
   self.password.center = passwordCenter;
 } completion: ^(BOOL finished) {
   [UIView animateWithDuration: 0.2 animations: ^{
     self.login.alpha = 1;
   }];
 }];

**
UIViewAnimationOptionRepeat //動畫迴圈執行
UIViewAnimationOptionAutoreverse //動畫在執行完畢後會反方向再執行一次
我們將這兩個引數傳入到上面密碼框出現動畫中,看看會有什麼效果(不同的引數使用|操作符一起傳入)
**

783864-b2274177fcd08ea2.gif
重複動畫
  • Transitioning

除了上面提到的這些效果,在檢視、圖片切換的時候,我們還能通過傳入下面的這些引數來實現一些特殊的動畫效果。


UIViewAnimationOptionTransitionNone //沒有效果,預設 UIViewAnimationOptionTransitionFlipFromLeft //從左翻轉效果 UIViewAnimationOptionTransitionFlipFromRight //從右翻轉效果 UIViewAnimationOptionTransitionCurlUp //從上往下翻頁 UIViewAnimationOptionTransitionCurlDown //從下往上翻頁 UIViewAnimationOptionTransitionCrossDissolve //舊檢視溶解過渡到下一個檢視
UIViewAnimationOptionTransitionFlipFromTop //從上翻轉效果 UIViewAnimationOptionTransitionFlipFromBottom //從上翻轉效果


那麼這些引數使用的時機是什麼時候呢?我們來看看這麼一段程式碼:

[UIView transitionWithView: firstPV duration: 0.5 options: UIViewAnimationOptionTransitionFlipFromLeft animations: ^{
 [firstPV flipCard];
} completion: ^(BOOL finished) {
 isAnimating = NO;
}];

- (void)flipCard{
 if (isfliped) { 
self.image = [UIImage imageNamed: @"flipPicBG.png"];
 isfliped = NO; 
} else { 
self.image = [UIImage imageNamed: [NSString stringWithFormat: @"flipPic%d.png", type]];
 isfliped = YES; 
}
}

這段程式碼中我改變了一個UIImageView的圖片顯示,同樣用了一個動畫的方式表現。這裡用到了一個新的動畫API方法:transitionWithView: duration: options: animations: completion:。這個方法跟上面的animateWithDuration系列方法相比多了一個UIView型別的引數,這個引數接收的物件作為動畫的作用者。這段程式碼是我以前做的一個翻卡匹配的小遊戲,點選之後的動畫效果如下:

1400111-a00c1503d593f96a.gif
翻卡匹配小遊戲

在模擬器下使用command+T
放慢了動畫的速度之後,我擷取了翻轉的四張圖片:
慢動作翻轉

在我們切換圖片的時候,原有的圖片會基於檢視中心位置進行x軸上的翻轉,為了達到更逼真的效果,系統還為我們在切換中加上了陰影效果(ps: 再次要說明的是,transition的動畫你應該只用在檢視的切換當中 —— 你不會在移動中產生任何transition效果的)。

** 這是一個便捷的檢視過渡 API,在動畫過程中,首先將 fromView 從父檢視中刪除,然後將 toView 新增,就是做了一個替換操作。**

在需要檢視更改時,這個將變得特別有用。

+ (void)transitionWithView:(UIView )view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ nullable)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;
+ (void)transitionFromView:(UIView )fromView toView:(UIView )toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ nullable)(BOOL finished))completion ;
  • transform動畫

是一個非常重要的屬性,它在矩陣變換的層面上改變檢視的顯示效果,完成旋轉、形變、平移等等操作。在它被修改的同時,檢視的frame也會被真實改變。有兩個資料型別用來表示transform,分別是CGAffineTransformCATransform3D。前者作用於UIView,後者為layer層次的變換型別。基於後者可以實現更加強大的功能,但我們需要先掌握CGAffineTransform型別的使用。同時,本文講解也是這個變換型別。
對於想要了解矩陣變換是如何作用實現的,可以參考這篇部落格:CGAffineTransform 放射變換

在開始使用transform
實現你的動畫之前,我先介紹幾個常用的函式:


建立一個仿射矩陣

CGAffineTransformMake 直接賦值來建立
CGAffineTransformMakeRotation 設定角度來生成矩陣
結果就是
CGAffineTransformMakeScale 設定縮放,及改變a、d的值
CGAffineTransformMakeTranslation 設定偏移
改變已經存在的放射矩陣

CGAffineTransformTranslate 原始的基礎上加上偏移
CGAffineTransformScale加上縮放
CGAffineTransformRotate加上旋轉
CGAffineTransformInvert 反向的仿射矩陣比如(x,y)通過矩陣t得到了(x',y')那麼通過這個函式生成的t'作用與(x',y')就能得到原始的(x,y)
CGAffineTransformConcat 通過兩個已經存在的放射矩陣生成一個新的矩陣t' = t1 * t2
應用仿射矩陣

CGPointApplyAffineTransform 得到新的點
CGSizeApplyAffineTransform 得到新的size
CGRectApplyAffineTransform 得到新的rect
評測矩陣

CGAffineTransformIsIdentity 是否是CGAffineTransformIsIdentity
CGAffineTransformEqualToTransform 看兩個矩陣是否相等


我把demo左下角文字的變形過程記錄下來。這裡推薦mac上面的一款擷取動圖的程式licecap,非常簡單好用。博主用它來分解動畫步驟,然後進行重現。

1400111-d0d1dfb20b6e8aeb.png
文字變形過程

不難看出在文字的動畫中做了兩個處理:y軸上的形變縮小、透明度的漸變過程。首先在專案中新增兩個UILabel,分別命名為label1、label2.然後在viewDidAppear中加入這麼一段程式碼:

- (void)viewDidAppear: (BOOL)animated {
 label1.transform = CGAffineTransformMakeScale(1, 0);
 label1.alpha = 0; 
[UIView animateWithDuration: 3. animations: ^ { label1.transform = CGAffineTransformMakeScale(1, 1);
 label2.transform = CGAffineTransformMakeScale(1, 0.1);
 label1.alpha = 1; label2.alpha = 0; }];
}

這裡解釋一下為什麼label2為什麼在動畫中y軸逐漸縮小為0.1而不是0。如果我們設為0的話,那麼在動畫提交之後,label2會直接保持動畫結束的狀態(這是出於效能優化自動完成的),因此在使用任何縮小的形變時,你可以將縮小值設定的很小,只要不是0。
執行你的程式碼,文字的形變過程你已經做出來了,但是demo中的動畫不僅僅是形變,還包括位移的過程。很顯然,我們可以通過改變center
的位置來實現這個效果,但這顯然不是我們今天想要的結果,實現新的動畫方式來實現更有意義。
動畫開始時形變出現的label高度為0,然後逐漸的的變高變為height
,而label從頭到尾基於頂部的位置不發生改變。因此動畫開始前這個label在y軸上的位置是0,在完成顯示之後的y軸中心點為height / 2
(基於label自身的座標系而言),那麼動畫的程式碼就可以寫成這樣:

- (void)viewDidAppear: (BOOL)animated { 
// 初始化動畫開始前label的位置 CGFloat offset = label1.frame.size.height * 0.5;
 label1.transform = CGAffineTransformConcat( CGAffineTransformMakeScale(0, 0), CGAffineTransformTranslate(0, -offset) ); 
label1.alpha = 0; 
[UIView animateWithDuration: 3. animations: ^ {
 // 還原label1的變換狀態並形變和偏移label2
 label1.transform = CGAffineTransformIdentifier; 
label1.transform = CGAffineTransformConcat( CGAffineTransformMakeScale(0, 0), CGAffineTransformTranslate(0, offset) ); 
label1.alpha = 1;
 label2.alpha = 0; }];}

調整兩個label的位置,並且設定其中一個透明顯示。然後執行這段程式碼,你會發現文字轉變過程的動畫完成了。

  • 彈簧動畫
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion

iOS 7.0 增加的方法,新添了兩個引數,springDamping
和initialSpringVelocity。
springDamping:彈性阻尼,取值範圍時 0 到 1,越接近 0 ,動畫的彈性效果就越明顯;如果設定為 1,則動畫不會有彈性效果。
initialSpringVelocity:檢視在動畫開始時的速度,>= 0。初始化速度,值越高則物品的速度越快

1400111-25b137bde716192e.gif
springDamping

在 initialSpringVelocity 為 0 ,damping 分別為 0.4,0.6,0.8 的情況下效果如上圖,可見阻尼越小,彈簧效果越明顯。

1400111-abad17dd6da3ba51.gif
initialSpringVelocity

在 damping 為 1 ,initialSpringVelocity 分別為 0,5,30 的情況下效果如上圖,可見初始速度越大彈簧效果越明顯,彈動的幅度越大。

3. UIViewKeyframeAnimations

  • 簡介

UIViewAnimationWithBlocks 推出於 iOS 7.0,用來實現幀動畫 。基礎動畫只能將 UIView的屬性從一個值變化到另一個值,而關鍵幀動畫可以包含任意一個關鍵幀,使 UIView在多個值之間進行變化。關鍵幀動畫可以看成是若干個連續執行的基礎動畫。

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ; 
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations ;

其中animateKeyframesWithDuration:delay:options:animations:completion:
引數的使用方法與基礎動畫大致相同,只是基礎動畫的 options 是UIViewAnimationOptions 型別,而關鍵幀動畫的 options 是UIViewKeyAnimationOptions 型別。另外,關鍵幀動畫的持續時間( duration )是整個動畫的持續時間,也就是所有關鍵幀持續時間的總和。addKeyframeWithRelativeStartTime:relativeDuration:animations:
中的第一個引數( relativeStartTime )是相對起始時間,表示該關鍵幀開始執行的時刻在整個動畫持續時間中的百分比,取值範圍是[0-1]。第二個引數( relativeDuration )是相對持續時間,表示該關鍵幀佔整個動畫持續時間的百分比,取值範圍也是[0-1]。

  • 程式碼示例

我們demo連結中的落葉動畫來,我總共對葉子的center進行過五次修改,我將落葉平移的線性路徑繪製出來並且標註關鍵的轉折點:

1400111-dd50df9a4bee988e.png
1.png

上面這個平移用UIView動畫程式碼要如何實現呢?毫無疑問,我們需要不斷的巢狀UIView動畫的使用來實現,具體程式碼如下:

[self moveLeafWithOffset: (CGPoint){ 15, 80 } completion: ^(BOOL finished) {
 [self moveLeafWithOffset: (CGPoint){ 30, 105 } completion: ^(BOOL finished) { 
[self moveLeafWithOffset: (CGPoint){ 40, 110 } completion: ^(BOOL finished) {
 [self moveLeafWithOffset: (CGPoint){ 90, 80 } completion: ^(BOOL finished) {
 [self moveLeafWithOffset: (CGPoint){ 80, 60 } completion: nil duration: 0.6]; 
} duration: 1.2]; 
} duration: 1.2]; 
} duration: 0.6];
} duration: 0.4];

- (void)moveLeafWithOffset: (CGPoint)offset completion: (void(^)(BOOL finished))completion duration: (NSTimeInterval)duration{
 [UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^{
 CGPoint center = _leaf.center;
 center.x += offset.x; 
center.y += offset.y;
 _leaf.center = center; 
} completion:
 completion];
}

看起來還蠻容易的,上面的程式碼只是移動葉子,在gif圖中我們的葉子還有旋轉,因此我們還需要加上這麼一段程式碼:

[UIView animateWithDuration: 4 animations: ^{ _leaf.transform = CGAffineTransformMakeRotation(M_PI);}];

那麼ok,執行這段程式碼看看,落葉的移動非常的生硬,我們可以明顯的看到拐角。其次,這段程式碼中的duration傳入是沒有任何意義的(傳入一個固定的動畫時長無法體現出在落葉飄下這一過程中的層次步驟)對於這兩個問題,UIView也提供了另一種動畫方式來幫助我們解決這兩個問題 —— keyframe動畫:

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations

第一個方法是建立一個關鍵幀動畫,第二個方法用於在動畫的程式碼塊中插入關鍵幀動畫資訊,兩個引數的意義表示如下:
frameStartTime 表示關鍵幀動畫開始的時刻在整個動畫中的百分比
frameDuration 表示這個關鍵幀動畫佔用整個動畫時長的百分比。

我做了一張圖片來表示引數含義:


1400111-4b2d2822774bf06e.png

新增關鍵幀方法引數說明

對比UIView動畫跟關鍵幀動畫,關鍵幀動畫引入了動畫佔比時長的概念,這讓我們能控制每個關鍵幀動畫的佔用比例而不是傳入一個無意義的動畫時長 —— 這讓我們的程式碼更加難以理解。當然,除了動畫佔比之外,關鍵幀動畫的options
引數也讓動畫變得更加平滑,下面是關鍵幀特有的配置引數:


// 連續運算模式,線性
UIViewKeyframeAnimationOptionCalculationModeLinear
// 離散運算模式,只顯示關鍵幀
UIViewKeyframeAnimationOptionCalculationModeDiscrete
// 均勻執行運算模式,線性
UIViewKeyframeAnimationOptionCalculationModePaced
// 平滑運算模式
UIViewKeyframeAnimationOptionCalculationModeCubic
// 平滑均勻運算模式
UIViewKeyframeAnimationOptionCalculationModeCubicPaced


Demo中我使用的是UIViewKeyframeAnimationOptionCalculationModeCubic
,這個引數使用了貝塞爾曲線讓落葉的下落動畫變得更加平滑。效果可見最開始的gif動畫,你可以修改demo傳入的不同引數來檢視效果。接下來我們就根據新的方法把上面的UIView動畫轉換成關鍵幀動畫程式碼,具體程式碼如下:

[UIView animateKeyframesWithDuration: 4 delay: 0 options: UIViewKeyframeAnimationOptionCalculationModeLinear animations: 
^{
 __block CGPoint center = _leaf.center;
 [UIView addKeyframeWithRelativeStartTime: 0 relativeDuration: 0.1 animations: ^{ _leaf.center = (CGPoint){ center.x + 15, center.y + 80 }; 
}];
 [UIView addKeyframeWithRelativeStartTime: 0.1 relativeDuration: 0.15 animations: ^{ 
_leaf.center = (CGPoint){ center.x + 45, center.y + 185 }; 
}];
 [UIView addKeyframeWithRelativeStartTime: 0.25 relativeDuration: 0.3 animations: ^{
 _leaf.center = (CGPoint){ center.x + 90, center.y + 295 }; 
}];
 [UIView addKeyframeWithRelativeStartTime: 0.55 relativeDuration: 0.3 animations: ^{ _leaf.center = (CGPoint){ center.x + 180, center.y + 375 }; 
}];
 [UIView addKeyframeWithRelativeStartTime: 0.85 relativeDuration: 0.15 animations: ^{
 _leaf.center = (CGPoint){ center.x + 260, center.y + 435 };
 }]; 
[UIView addKeyframeWithRelativeStartTime: 0 relativeDuration: 1 animations: ^{ _leaf.transform = CGAffineTransformMakeRotation(M_PI); 
}];
} completion: nil];

可以看到相比UIView的動畫,關鍵幀動畫更加直觀的讓我們明白每一次平移動畫的時間佔比,程式碼也相對的更加簡潔。

相關文章