2013-03-02 131 views
14

我是新來的Objective-C/iOS編程,我想了解UIView動畫是如何工作的。UIView動畫與塊如何工作

說我有這樣的代碼:

[UIView animateWithDuration:2.0 animations:^{ 
    self.label.alpha = 1.0; 
}]; 

是被作爲一個animations參數傳遞的東西是一個Objective-C塊(類似的lambda /其他語言的匿名函數),可以執行,那麼它將labelalpha屬性從當前值更改爲1.0。

但是,塊不接受動畫進度參數(比如說從0.0到1.0或從0到1000)。我的問題是動畫框架如何使用此塊來了解中間幀,因爲塊只指定最終狀態。

編輯: 我的問題是相當約animateWithDuration方法的引擎蓋操作,而不是的方式來使用它下。

我的animateWithDuration代碼是如何工作的假設如下:

  1. animateWithDuration激活某種特殊狀態中的變化沒有實際執行,但只有註冊的所有視圖對象。
  2. 它執行塊並更改已註冊。
  3. 它查詢視圖對象的狀態變化並獲取初始值和目標值,從而知道要更改哪些屬性以及執行更改的範圍。
  4. 它根據持續時間和初始/目標值計算中間幀,並觸發動畫。

有人可以說清楚animateWithDuration是否真的能以這種方式工作嗎?

回答

16

當然,我不知道是什麼引擎蓋下到底會發生因爲UIKit的不是開源的,我不會在蘋果工作,但這裏有一些想法:

基於塊的UIView之前引入動畫方式,動畫的觀點看起來像這一點,這些方法實際上仍然可用:

[UIView beginAnimations:nil context:nil]; 
[UIView setAnimationDuration:duration]; 
myView.center = CGPointMake(300, 300); 
[UIView commitAnimations]; 

認識到這一點,我們可以實現我們自己的基於塊的動畫製作方法是這樣的:

+ (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
{ 
    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:duration]; 
    animations(); 
    [UIView commitAnimations]; 
} 

......這與現有的animateWithDuration:animations:方法完全相同。

將塊排除在等式之外,很明顯,必須有某種全局動畫狀態,UIView隨後會在動畫塊內完成動畫變化(動畫)屬性的動畫。這必須是某種堆棧,因爲你可以嵌套動畫塊。

實際的動畫是通過核心動畫,這在層級進行工作 - 每個UIView有後盾CALayer實例,它是負責動畫和合成,而鑑於大多隻是處理觸摸事件和座標系統轉換。

我不會在這裏詳細介紹Core Animation的工作原理,您可能需要閱讀Core Animation Programming Guide。實質上,它是一個系統,可以在層次樹中動畫變化,而無需明確計算每個關鍵幀(實際上,從Core Animation獲取中間值實際上相當困難,通常只需指定值和值,持續時間等,然後讓系統照顧細節)。

因爲UIView基於CALayer,其許多屬性實際上是在底層實現的。例如,當您設置或獲得view.center時,與view.layer.location相同,更改其中任一個也會更改另一個。

層可以CAAnimation明確動畫(其是具有許多具體的實現,如用於CABasicAnimation簡單的事情和CAKeyframeAnimation用於更復雜的東西一個抽象類)。

那麼一個UIView屬性設置器可以做什麼來實現動畫塊中的「神奇」動畫變化動畫?讓我們看看我們是否可以重新實現其中之一,爲了簡單起見,讓我們使用setCenter:

首先,這裏從上面使用全局CATransactionmy_animateWithDuration:animations:方法的修改版本,這樣我們就可以在我們的setCenter:方法找出動畫應該多久採取:

- (void)my_animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 
{ 
    [CATransaction begin]; 
    [CATransaction setAnimationDuration:duration]; 

    animations(); 

    [CATransaction commit]; 
} 

注意我們不再使用beginAnimations:...commitAnimations,所以沒有做任何事情,什麼都不會動畫。

現在,讓我們來覆蓋setCenter:UIView子類:

@interface MyView : UIView 
@end 

@implementation MyView 
- (void)setCenter:(CGPoint)position 
{ 
    if ([CATransaction animationDuration] > 0) { 
     CALayer *layer = self.layer; 
     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
     animation.fromValue = [layer valueForKey:@"position"]; 
     animation.toValue = [NSValue valueWithCGPoint:position]; 
     layer.position = position; 
     [layer addAnimation:animation forKey:@"position"]; 
    } 
} 
@end 

在這裏,我們建立了一個明確的動畫使用的Core Animation動畫是底層的location財產。動畫的持續時間將自動從CATransaction中獲取。讓我們來嘗試一下:

MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 
myView.backgroundColor = [UIColor redColor]; 
[self.view addSubview:myView]; 

[self my_animateWithDuration:4.0 animations:^{ 
    NSLog(@"center before: %@", NSStringFromCGPoint(myView.center)); 
    myView.center = CGPointMake(300, 300); 
    NSLog(@"center after : %@", NSStringFromCGPoint(myView.center)); 
}]; 

我不是說這正是UIView動畫系統是如何工作的,它只是說明如何它可以原則工作。

1

未指定中間幀的值;在之前設置的值和動畫塊內設置的目標值之間自動生成值的動畫(在這種情況下爲alpha,但也包括顏色,位置等)。您可以通過使用animateWithDuration:delay:options:animations:completion:指定選項來影響曲線(默認值爲UIViewAnimationOptionCurveEaseInOut,即值更改的速度將加速和減速)。

請注意,任何先前設置的值的動畫更改將首先完成,即每個動畫塊指定從前一個結束值到新的新動畫。您可以指定UIViewAnimationOptionBeginFromCurrentState從已經進行中的動畫狀態開始新的動畫。

0

這會將塊內指定的屬性從當前值更改爲您提供的任何值,並且會在整個持續時間內線性地執行此操作。

因此,如果原始alpha值爲0,則會在2秒內褪色標籤。如果原始值已經是1.0,則根本看不到任何效果。

在引擎蓋下,UIView負責確定需要發生更改的動畫幀數。

您還可以通過將緩動曲線指定爲UIViewAnimationOption來更改發生更改的速率。再次,UIView爲您處理補間。