web-dev-qa-db-ja.com

iOSアプリケーションでさまざまなCAAnimationをチェーンする方法

アニメーション、CABasicAnimationまたはCAAnimationGroupをチェーンする必要がありますが、その方法がわかりません。唯一の方法は、すべてのアニメーションが同じレイヤーに対して同時に実行されることです。

どうすればいいですか?

たとえば、コンテンツが車の画像に設定されているレイヤー:

1番目:Xポイントを右に移動します

2番目:90ªを左に回転します

3番目:Xポイントを移動

4番目:レイヤーをスケーリングします

このアニメーションはすべて秘密裏に実行する必要がありますが、実行できません:S

ところで:私は英語ではありません。文法を間違えたらごめんなさい:D

21
torhector2

tl; dr:前回の終了後に、各アニメーションを手動で追加する必要があります。


シーケンシャルアニメーションを追加する組み込みの方法はありません。あなたcouldは、各アニメーションの遅延を以前のすべてのアニメーションの合計に設定できましたが、お勧めしません。

代わりに、すべてのアニメーションを作成し、実行する予定の順序で(配列をキューとして使用して)可変配列に追加します。次に、すべてのアニメーションへのアニメーションデリゲートとして自分自身を設定することにより、アニメーションが終了するたびにanimationDidStop:finished:コールバックを取得できます。

そのメソッドでは、最初のアニメーション(次のアニメーションを意味する)を配列から削除し、それをレイヤーに追加します。あなたはデリゲートであるため、アニメーションが終了すると2番目のアニメーションが表示されます。その場合、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:だけで機能します)。

20

Davidが提案することは問題なく機能しますが、別の方法をお勧めします。

すべてのアニメーションが同じレイヤーにある場合は、アニメーショングループを作成し、各アニメーションに異なる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;
33
Duncan C

Swiftのソリューションは次のとおりです。

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)
4
Kevin

DavidのアプローチまたはbeginTimeプロパティのいずれかが、アニメーションの連鎖に問題ありません。 http://wangling.me/2011/06/time-warp-in-animation.html を参照してください-beginTimeなどの使用法を明確にしていますCAMediaTimingプロトコルプロパティ。

1
Rodolfo

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];
    }
}
0