甘いものが好きです

iOS App開発時に感じた疑問や課題、その他の雑感などを書いていきます。

CAAnimation終了時にアニメーションを区別して追加処理を行う

Core Animationを使用してアニメーションを実装する際に、アニメーション終了時に追加処理を実行させたいことがある。例えば、あるImageViewを回転させるアニメーションを行い回転終了時にその角度のまま静止させる、といったことをやりたい場合を考えてみよう。この場合、まずは回転対象のImageViewのレイヤーにaddAnimation:forKey:で回転アニメーションを追加し、デリゲートメソッドanimationDidStop:finished:内にアニメーション終了時の処理を記述する。

下のソースコード内にもコメントで記述しているが、この処理を実装する際にはいくつかの注意すべき点がある。

  • アニメーションオブジェクトのdelegateを設定する。これを設定しなければ、デリゲートのanimationDidStop:finished:が呼ばれることはないので、アニメーション終了時の処理が実行されなくなってしまう。
  • アニメーションオブジェクトのプロパティremovedOnCompletionにNOを設定する。このプロパティのデフォルトはYESなので、ユーザが明示的にNOを設定しなければアニメーション終了時にはこのアニメーションオブジェクトが自動的に削除されてしまい、animationDidStop:finished:で参照できなくなる。

複数のアニメーションオブジェクトのデリゲートに同じオブジェクトを設定すると、それらのアニメーションが終了するごとにanimationDidStop:finished:が一度ずつコールされる。場合によっては、animationDidStop:finished:内でアニメーションを区別し、それぞれのアニメーションごとに異なる処理を行いたいこともあるだろう。このような場合には、レイヤーに対してキーごとにanimationForKey:でアニメーションを問い合わせ、それにより取得したアニメーションオブジェクトをanimationDidStop:finished:の第1引数と比較することで、アニメーションを区別することができる。

- (void)sampleAnimation {
    CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
    
    // これを設定しなければanimationDidStop:finished:は呼ばれない。
    rotateAnimation.delegate = self;

    // これを設定しなければanimationDidStop:finished:が呼ばれた時点で
    // このアニメーションはレイヤーから取り除かれている。
    rotateAnimation.removedOnCompletion = NO;

    rotateAnimation.duration = 1.0;
    rotateAnimation.repeatCount = 0;
    CGFloat angle = 30.0;
    CATransform3D rotateTransform = CATransform3DMakeRotation(angle * M_PI / 180.0, 0, 0, 1.0);
    rotateAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    rotateAnimation.toValue = [NSValue valueWithCATransform3D:rotateTransform];
    [imageView.layer addAnimation:rotateAnimation forKey:@"rotateAnimation"];
}

// アニメーション終了時にこのメソッドがコールされる。
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

    // 複数のアニメーションのdelegateに同じオブジェクトを設定している場合には、
    // 引数animが処理対象のアニメーションと一致するかをチェックし、
    // 一致した場合にのみ処理を行う。
    if (anim == [imageView.layer animationForKey:@"rotateAnimation"]) {
        // アニメーション終了時の状態で静止させるためには
        // アニメーションのtoValueプロパティを使用すればよい。
        CABasicAnimation *basicAnim = (CABasicAnimation *)anim;
        imageView.layer.transform = [basicAnim.toValue CATransform3DValue];
        [imageView.layer removeAnimationForKey:@"rotateAnimation"];
    }
}