2012-02-21 106 views
13

我有一種感覺,我忽略了一些基本的東西,但是找到它比在互聯網上錯誤的更好的方法是什麼?CAGradientLayer屬性不在UIView動畫塊內動畫

我有一個相當基本的用戶界面。我的UIViewController的視圖是+layerClassCAGradientLayer的子類。根據用戶的操作,我需要移動一些UI元素,並更改背景漸變的值。該代碼看起來是這樣的:

[UIView animateWithDuration:0.3 animations:^{ 
    self.subview1.frame = CGRectMake(...); 
    self.subview2.frame = CGRectMake(...); 
    self.subview2.alpha = 0; 

    NSArray* newColors = [NSArray arrayWithObjects: 
         (id)firstColor.CGColor, 
         (id)secondColor.CGColor, 
         nil]; 
    [(CAGradientLayer *)self.layer setColors:newColors]; 
}]; 

的問題是,我在這塊子視圖改變動畫就好了(東西移動和淡入淡出),但改變漸變的顏色沒有。它只是互換。

現在,the documentation does say動畫塊中的Core Animation代碼不會繼承塊的屬性(持續時間,緩動等)。但是,這種情況是不是完全定義了動畫交易? (文檔的含義似乎是,你會得到一個默認的動畫,我沒有得到。)

我是否使用明確的CAAnimation使這項工作? (如果是這樣,爲什麼?)

回答

13

這裏似乎有兩件事情要做。第一個(正如Travis正確指出的那樣,並且文檔狀態)是UIKit動畫似乎不適用於應用於CALayer屬性更改的隱式動畫。我認爲這很奇怪(UIKit 必須使用核心動畫),但它是什麼。

這裏有一個(可能是很愚蠢?)解決方法這個問題:

NSTimeInterval duration = 2.0; // slow things down for ease of debugging 
    [UIView animateWithDuration:duration animations:^{ 
    [CATransaction begin]; 
    [CATransaction setAnimationDuration:duration]; 

    // ... do stuff to things here ... 

    [CATransaction commit]; 
    }]; 

的另一個關鍵是,這個梯度層是我的看法的層。這意味着我的視圖是圖層的委託(如果漸變圖層只是一個子圖層,它不會有委託)。對於"colors"事件,-actionForLayer:forKey:UIView實現返回NSNull。 (可能每個事件都不在UIView動畫的特定列表中。)

添加以下代碼,以我的觀點會引起顏色變化預計將動畫:

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event 
{ 
    id<CAAction> action = [super actionForLayer:layer forKey:event]; 
    if([@"colors" isEqualToString:event] 
     && (nil == action || (id)[NSNull null] == action)) { 
    action = [CABasicAnimation animationWithKeyPath:event]; 
    } 
    return action; 
} 
+0

神奇的發現!從來沒有猜到這一點。 – 2012-08-31 04:07:13

1

您必須使用顯式CAAnimations,因爲您正在更改CALayer的值。 UIViewAnimations上的UIView屬性的作用,而不是直接在他們的CALayer的屬性...

其實,你應該使用CABasicAnimation,這樣就可以訪問它fromValuetoValue性能。

下面的代碼應該爲你工作:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    [UIView animateWithDuration:2.0f 
          delay:0.0f 
         options:UIViewAnimationCurveEaseInOut 
        animations:^{ 
         CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"]; 
         animation.duration = 2.0f; 
         animation.delegate = self; 
         animation.fromValue = ((CAGradientLayer *)self.layer).colors; 
         animation.toValue = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor,(id)[UIColor whiteColor].CGColor,nil]; 
         [self.layer addAnimation:animation forKey:@"animateColors"]; 
        } 
        completion:nil]; 
} 

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
    NSString *keyPath = ((CAPropertyAnimation *)anim).keyPath; 
    if ([keyPath isEqualToString:@"colors"]) { 
     ((CAGradientLayer *)self.layer).colors = ((CABasicAnimation *)anim).toValue; 
    } 
} 

有與該CAAnimations一招,你必須明確地設置該屬性的值在完成動畫。

您可以通過設置委託來完成此操作,在這種情況下,我將其設置爲調用動畫的對象,然後重寫其animationDidStop:finished:方法以將CAG漸變圖層的顏色設置爲其最終值。

您還需要在animationDidStop:方法中執行一些操作以訪問動畫的屬性。

+0

的事情是,CALayer的屬性有隱式動畫。它似乎是由UIView動畫定義的動畫屬性不適用於CALayer更改,但這並不能解釋爲什麼我沒有看到任何*動畫的顏色變化。 – 2012-02-28 17:33:39

+0

UIView和CALayer都有隱式動畫。當你觸發一個UIViewAnimation,並且你想要在它的CALayer上觸發一個動畫時,你必須同時明確地這樣做。這就是CABasicAnimation在UIView動畫中所做的事情:^ {} block ... – 2012-02-28 19:40:47

+0

但關鍵是隱式動畫不會發生,因爲所討論的圖層是* view的*圖層。從UIView動畫塊內部進行更改是完全不同的問題。 (隱式動畫的全部要點是它們不需要明確的動畫塊。) – 2012-02-28 22:36:19