2012-07-31 64 views
17

我需要鏈接動畫,CABasicAnimation或CAAnimationGroup,但我不知道該怎麼做,我所做的唯一的事情就是所有動畫同時執行相同的操作層。如何在iOS應用程序中鏈接不同的CAAnimation

我該怎麼辦?

例如,具有其內容的層設置爲一個汽車圖像:

第一:移動X點至右

第二:旋轉90ª向左

第三:移動X點

第4步:縮放圖層

所有這些動畫必須以非常精確的方式執行,但我做不到:S

BTW:我不是英語,對不起,如果我改變了我的語法一些錯誤:d

+0

你打算做更高級的動畫以及或僅移動,縮放和旋轉(不是3D)? – 2012-07-31 10:28:15

+0

是的,我也使用CATransform3DMakeRotation和其他人 – torhector2 2012-07-31 10:48:25

回答

16

TL;博士:您需要以前完成後手動添加每個動畫。


沒有內置的方式來添加順序動畫。你可能設置每個動畫的延遲是所有以前的動畫的總和,但我不會推薦它。

相反,我會創建所有的動畫,並將它們按照它們應該運行的順序添加到可變數組中(使用數組作爲隊列)。然後,通過將自己設置爲所有動畫的動畫代理,只要動畫完成,就可以獲取animationDidStop:finished:回調。

在該方法中,您將從數組中刪除第一個動畫(意味着下一個動畫)並將其添加到圖層中。由於您是委託人,因此您將獲得第二個動畫,在這種情況下,animationDidStop:finished:回調將再次運行,並將下一個動畫從可變陣列中移除並添加到圖層。

一旦動畫數組爲空,所有動畫都會運行。


一些示例代碼,讓你開始。首先,你設置你所有的動畫:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; 
[animation setToValue:(id)[[UIColor redColor] CGColor]]; 
[animation setDuration:1.5]; 
[animation setDelegate:self]; 
[animation setValue:[view layer] forKey:@"layerToApplyAnimationTo"]; 

// Configure other animations the same way ... 

[self setSequenceOfAnimations:[NSMutableArray arrayWithArray: @[ animation, animation1, animation2, animation3, animation4, animation5 ] ]]; 

// Start the chain of animations by adding the "next" (the first) animation 
[self applyNextAnimation]; 

然後在委託回調您簡單地套用一個動畫再次

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished { 
    [self applyNextAnimation]; 
} 

- (void)applyNextAnimation { 
    // Finish when there are no more animations to run 
    if ([[self sequenceOfAnimations] count] == 0) return; 

    // Get the next animation and remove it from the "queue" 
    CAPropertyAnimation * nextAnimation = [[self sequenceOfAnimations] objectAtIndex:0]; 
    [[self sequenceOfAnimations] removeObjectAtIndex:0]; 

    // Get the layer and apply the animation 
    CALayer *layerToAnimate = [nextAnimation valueForKey:@"layerToApplyAnimationTo"]; 
    [layerToAnimate addAnimation:nextAnimation forKey:nil]; 
} 

我使用的是自定義鍵layerToApplyAnimationTo使每個動畫知道它的層(它僅適用於setValue:forKey:valueForKey:)。

+0

大衛,你怎麼不喜歡爲每個動畫設置不同的beginTime值?編寫代碼通常比使用完成方法鏈接動畫更簡單,而且它完全符合我的經驗。 – 2012-07-31 20:31:43

+1

@DuncanC它感覺脆弱的我。添加,刪除或更改動畫的順序變得更麻煩並且容易出錯。 – 2012-08-01 05:25:22

+1

非常感謝你,我認爲這兩個答案對我來說都是有效的,但大衛對我的看法更好。 – torhector2 2012-08-01 09:30:53

28

什麼大衛建議工作正常,但我會建議一種不同的方式。

如果所有動畫都在同一圖層上,則可以創建一個動畫組,並使每個動畫具有不同的beginTime,其中第一個動畫開始於beginTime 0,並且每個新動畫在動畫之前。

但是,如果您的動畫在不同的圖層上,則不能使用動畫組(動畫組中的所有動畫必須在同一圖層上執行操作)。在這種情況下,您需要提交單獨的動畫,其中有一個beginTime是從CACurrentMediaTime()補償,例如:

CGFloat totalDuration = 0; 
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath: @"alpha"]; 
animationOne.beginTime = CACurrentMediaTime(); //Start instantly. 
animationOne.duration = animationOneDuration; 
... 
//add animation to layer 

totalDuration += animationOneDuration; 

CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath: @"position"]; 
animationTwo.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation one. 
animationTwo.duration = animationTwoDuration; 
... 
//add animation to layer 

totalDuration += animationTwoDuration; 


CABasicAnimation *animationThree = [CABasicAnimation animationWithKeyPath: @"position"]; 
animationThree.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation three. 
animationThree.duration = animationThreeDuration; 
... 
//add animation to layer 

totalDuration += animationThreeDuration; 
+5

如果這有助於其他人:如果你使用動畫組(即不是這裏的代碼片段,但大衛的第一段),那麼animation.beginTim是相對於組... ....所以你不把它設置爲CACurrentMediaTime(),你只需將它設置爲totalDuration。這讓我感到困惑了幾分鐘.... – xaphod 2015-06-06 09:38:00

0

使用KVC。每個動畫的關鍵字setValue。之後,在animationDidStop中,您可以定義哪些動畫已停止並在鏈中運行。

- (void)moveXrightAnimation { 
 
    CABasicAnimation* theAnimation = ... 
 
    [theAnimation setValue:@"movexright" forKey:@"animationID"]; 
 
//animation 
 
} 
 

 
- (void)rotate90leftAnimation { 
 
    CABasicAnimation* theAnimation = ... 
 
    [theAnimation setValue:@"rotate90left" forKey:@"animationID"]; 
 
//animation 
 
} 
 

 
- (void)moveXpointAnimation { 
 
    CABasicAnimation* theAnimation = ... 
 
    [theAnimation setValue:@"movexpoint" forKey:@"animationID"]; 
 
//animation 
 
} 
 

 
- (void)scaleAnimation { 
 
    CABasicAnimation* theAnimation = ... 
 
    [theAnimation setValue:@"scale" forKey:@"animationID"]; 
 
//animation 
 
} 
 

 
#pragma mark - CAAnimationDelegate 
 

 
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
 
    
 
    if([[anim valueForKey:@"animationID"] isEqual:@"movexright"]) { 
 
     [self rotate90leftAnimation]; 
 
    } 
 
    if([[anim valueForKey:@"animationID"] isEqual:@"rotate90left"]) { 
 
     [self moveXpointAnimation]; 
 
    } 
 
    if([[anim valueForKey:@"animationID"] isEqual:@"movexpoint"]) { 
 
     [self scaleAnimation]; 
 
    } 
 
}

1

下面是斯威夫特的解決方案:

var animations = [CABasicAnimation]() 

var animation1 = CABasicAnimation(keyPath: "key_path_1") 
// animation set up here, I've included a few properties as an example 
animation1.duration = 1.0 
animation1.fromValue = 1 
animation1.toValue = 0 
animation1.append(animation1) 

var animation2 = CABasicAnimation(keyPath: "key_path_2") 
animation2.duration = 1.0 
animation2.fromValue = 1 
animation2.toValue = 0 
// setting beginTime is the key to chaining these 
animation2.beginTime = 1.0 
animations.append(animation2) 

let group = CAAnimationGroup() 
group.duration = 2.0 
group.repeatCount = FLT_MAX 
group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 
group.animations = animations 

yourLayer.addAnimation(group, forKey: nil) 
相關問題