2010-07-19 96 views
7

我一直在試圖瞭解我的動畫有什麼問題,我仍然沒有弄明白。我認爲它應該是非常簡單的,但即使在閱讀了大量的例子和文檔之後,我可能仍然缺少某些東西。CABasicAnimation沒有動畫我的財產

我的問題最初形成的事實是,在iPhone上,您無法自動調整圖層大小(使用視圖)。文檔中另有說明,但SDK中沒有autoresizeMask層。所以我決定自己製作一個解決方法併爲該圖層設置動畫。

我有這段簡單的代碼,應該做一個簡單的調整大小的動畫。值是好的,我甚至設置委託爲了追蹤如果動畫開始/結束。

// I've got a property named layerImage (which is a CALayer) 
- (void)animateTestWithFrame:(CGRect)value { 
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"layerImage.frame"]; 
    animation.duration = 1; 
    animation.fromValue = [NSValue valueWithCGRect:self.frame]; 
    animation.toValue = [NSValue valueWithCGRect:value]; 
    animation.removedOnCompletion = YES; 
    animation.delegate = self; 

    [self.layer addAnimation:animation forKey:@"layerImage.frame"]; 
} 

那麼,有什麼想法? (包含代碼這一觀點是窗口的子視圖的子視圖如果能夠有所作爲)

---編輯---

似乎幀不通過CABasicAnimation和命名動畫財產「框架」。當使用邊界時,我有一些奇怪的結果,但至少我得到了一些東西。將繼續對此進行調查。

回答

26

所以你很好,你已經弄清楚了這裏的事情,但你對自己的問題的答案有一些不準確的地方。讓我糾正幾件事:

框架屬性可以被動畫 - 只是沒有顯式動畫。如果您做以下不是一個視圖的根層的層,框架將動畫就好了:

[CATransaction begin]; 
[CATransaction setAnimationDuration:2.0f]; 
[animationLayer setFrame:CGRectMake(100.0f, 100.0f, 100.0f, 100.0f)]; 
[CATransaction commit]; 

記住一個層上設置屬性將動畫默認該屬性的變化。事實上,如果你不想讓它動起來,你必須禁用屬性更改的動畫。 (當然,這隻有在您爲視圖的根層以外的其他圖層設置動畫時纔是如此。)如果您需要使用顯式動畫,則在動畫設置位置和邊界時是正確的。

使用隱式動畫可以動畫上一個UIView幀:

[UIView beginAnimations:nil context:NULL]; 
[UIView setAnimationDuration:3.0f]; 
[[self view] setFrame:CGRectMake(45.0f, 45.0f, 100.0f, 100.0f)]; 
[UIView commitAnimations]; 

這將從該視圖的當前幀的動畫(邊界和位置)至x = 45.0,Y = 45.0,W = 100.0 ,h = 100.0。

看來你也可能會誤解動畫和圖層之間的區別。您將動畫添加到圖層,但向圖層添加動畫不會自動設置您正在動畫的屬性。

CALayers是模型對象。它們包含關於最終渲染到屏幕的圖層的信息。如果您希望該屬性實際上具有該值,則您必須必須設置圖層的屬性。如果您只是爲屬性設置動畫效果,那麼它只會是一種可視化表示,而不是實際的 - 也就是說,這就是爲什麼值會快速恢復到圖層的原始值,因爲您從未真正改變過它。

這導致我到下一個點。你說:

使用 「animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;」確保 值不會在動畫

這結束 reseted是不完全正確的。這兩個值只是簡單地使動畫保持在最終位置可視,但是,該圖層的實際值沒有改變。它們仍然與開始動畫時的值完全相同。一般情況下(除少數例外),您不想使用這兩個參數,因爲它們只是只可視爲。你想要的是實際設置你的動畫屬性的圖層值。

舉例來說,你想要使用明確的動畫來動畫你的圖層的位置。這裏是你想要的代碼:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
[animation setFromValue:[NSValue valueWithCGPoint:CGPointMake(70.0f, 70.0f)]]; 
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(150.0f, 150.0f)]]; 
[animation setDuration:2.0f]; 

// Actually set the position on the *layer* that you want it to be 
// when the animation finishes. 
[animationLayer setPosition:CGPointMake(150.0f, 150.0f)]; 

// Add the animation for the position key to ensure that you 
// override the animation for position changes with your animation. 
[animationLayer addAnimation:animation forKey:@"position"]; 

你可能還想考慮動畫分組。通過動畫組,您可以將多個動畫組合在一起,然後控制它們之間的相互關係。在你的情況下,你的界限和位置動畫的持續時間是相同的,所以你試圖做的沒有一個組會工作得很好,但是如果你想抵消動畫的開始,例如你不想要位置動畫開始,直到第二個或第二個進入框架動畫,您可以通過設置beginTime位置動畫的值來錯開它們。

最後,我很想知道爲什麼你不能使用UIView提供的隱式動畫。我在絕大多數我寫的Core Animation代碼中使用這些代碼,並且無法明白爲什麼這不適合您的情況。

此致敬禮。

+0

感謝您的解釋馬特。但對於UIView動畫問題,它僅與我的圖層有關。通常,我使用UIView的動畫沒有問題,但在我的情況下,它造成了奇怪的行爲。調整視圖框的大小並不會調整iPhone的圖層大小(因爲iPhone上沒有自動機制,僅適用於視圖和子視圖)。所以,你必須「手動」。 – Sauleil 2010-07-21 14:00:14

+0

好的答案!很詳細。如果可以的話,我會給你2分。 – Soberman 2016-01-18 22:26:12

3

關鍵路徑應該只是屬性的關鍵路徑,而不是對象的名稱。

使用此

[CABasicAnimation animationWithKeyPath:@"frame"] 

,而不是這個

[CABasicAnimation animationWithKeyPath:@"layerImage.frame"] 

而就順便說一句,當你添加動畫層,關鍵doen't意味着關鍵屬性動畫。只是你想要這個動畫的關鍵(名字)(這是指你的代碼的最後一行)

+0

確定框架,但CABasicAnimation如何知道哪一層來動畫該屬性?這是否意味着我必須做[layerImage addAnimation:animation ...]。 (嘗試它,也不工作) – Sauleil 2010-07-19 19:41:25

+0

如果你在一個不同的對象上進行動畫(在該方法中),只需使用[self.layerImage.layer addAnimation:animation ...] – tadejsv 2010-07-19 19:49:52

+0

還有一件事:I不明白你爲什麼在這裏使用CABasicAnimation。 UIView的動畫可以。 – tadejsv 2010-07-19 19:52:12

1

所以,解決方案是動畫層的@「邊界」和@「位置」,因爲幀在iPhone上不是可以動畫的。我花了一些時間才明白,位置是圖層的中心,邊界的調整大小從中心延伸,但這很容易。

所以,我在簡歷所做的是:

  1. 在SETFRAME,創建2個動畫與邊界和位置屬性。
  2. 使用「animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;」以確保值不會在動畫結束時重置
  3. 將委託註冊爲self以便實現「animationDidStop:finished:」。看來你仍然需要設置值:「layerImage.bounds = [animation.toValue CGRectValue]; layerImage.position = [animation.toValue CGPointValue];」。

我無法直接使用UIView動畫系統,因爲它沒有在圖層上做我想做的。

感謝tadej5553指出我與「addAnimation」有關的圖層問題。所以這裏是那些想看看它的樣子的代碼。

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
    CABasicAnimation *animation = (CABasicAnimation*)anim; 

    if ([animation.keyPath isEqualToString:@"bounds"]) { 
     layerImage.bounds = [animation.toValue CGRectValue]; 
    } else if ([animation.keyPath isEqualToString:@"position"]) { 
     layerImage.position = [animation.toValue CGPointValue]; 
    } 
} 

- (void)setFrame:(CGRect)value { 
    CGRect bounds = CGRectMake(0, 0, value.size.width, value.size.height); 

    if ([UIView isAnimationStarted]) { 
     // animate the bounds 
     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"]; 
     animation.duration = [UIView animationDuration]; 
     animation.fromValue = [NSValue valueWithCGRect:layerImage.bounds]; 
     animation.toValue = [NSValue valueWithCGRect:bounds]; 
     animation.removedOnCompletion = NO; 
     animation.fillMode = kCAFillModeForwards; 
     animation.timingFunction = [UIView animationFunction]; 
     animation.delegate = self; 
     [layerImage addAnimation:animation forKey:@"BoundsAnimation"]; 

     // animate the position so it stays at 0, 0 of the frame. 
     animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
     animation.duration = [UIView animationDuration]; 
     animation.fromValue = [NSValue valueWithCGPoint:layerImage.position]; 
     animation.toValue = [NSValue valueWithCGPoint:CGPointMake(bounds.size.width/2, bounds.size.height/2)]; 
     animation.removedOnCompletion = NO; 
     animation.fillMode = kCAFillModeForwards; 
     animation.timingFunction = [UIView animationFunction]; 
     animation.delegate = self; 
     [layerImage addAnimation:animation forKey:@"PositionAnimation"]; 
    } else { 
     layerImage.frame = bounds; 
    } 

    [super setFrame:value]; 
}