[iOS]CoreAnimationでお手軽にシーケンシャルなアニメーションを実装する


##はじめに

###説明すること
アニメーションを順次再生するのに便利っぽいカテゴリを作ったので、そのご紹介をしつつシーケンシャルなアニメーションをどう実装するかについて説明します。

###説明しないこと
ここではCoreAnimationとかCABasicAnimation自体の説明はしてないので、そもそもアニメーション自体どうやって実装するの？って場合はまず以下の様な記事を読んでみると参考になると思います。


Objective-Cでアニメーションまとめ
http://qiita.com/edo_m18/items/8064a9c0102aac5ab415#2-4

Core Animation 中級編
http://qiita.com/inamiy/items/bdc0eb403852178c4ea7

## beginTimeを良きにはからってくれるやつを作った
結論から言うと、CABasicAnimationを利用して複数のアニメーションを順次実行させたい場合beginTimeというプロパティを変更することでアニメーションの開始時間を変更することが出来ます。ようするに、アニメーション毎にうまいこと開始時刻を設定すると順番に再生されるってワケ。

シンプル目なアニメーションならまぁ都度beginTimeを調整するのもアリなんですが、複雑なアニメーションになってくると、つらみがあるし、順番入れ替えるのとか面倒だし、そもそも人間がやる作業ではない。

というわけで、良きにはからってbeginTimeを調整してくれるCAAnimationGroupのカテゴリを作成しました。利用する場合は、以下から落としてご利用くださいませ。

https://github.com/kazu0620/CAAnimationGroup-DurationAdjuster

##使い方
上述のカテゴリは、CAAnimationGroupのカテゴリです。以下の操作を行うことで、個々のアニメーションのbeginTimeを意識することなくシーケンシャルなアニメーションを作ることが出来ます。

2. 新しくCAAnimationGroupを作成し、複数のアニメーションを登録する。
3. カテゴリによって生やされたCAAnimationGroup のメソッドsetDurationToSequentiallyを叩く。
4. アニメーションを再生すると、シーケンシャルに再生される。

##実装例:シンプルなアニメーションの例
というわけで、以下が実装例です。四角い正方形がフェードイン、フェードインが完了したら移動する、という簡単なアニメーションを作成してみましょう。

重要なのは、CAAnimationGroupを作成してる部分と、作成したグループに対して、setDurationToSequentiallyを叩いてる部分になります。

ようするにCAAnimationGroupに順次再生させたいアニメーションをポイポイと放り込んで、setDurationToSequentiallyってメソッドを叩くと、前から順番にアニメーションを再生するってワケですね。

```objectivec:ViewController.m
// カテゴリを読み込んでおく
#import "CAAnimationGroup+DurationAdjuster.h"

- (void) playAnimation {
    // アニメーションで動かすためのViewを作成
    UIView *redSquare = [[UIView alloc]
         initWithFrame:
         CGRectMake(100,300,50,50)
    ];
    redSquare.backgroundColor = [UIColor redColor];
    [self.view addSubview:redSquare];
   
    // フェードアニメーションを実装
    CABasicAnimation* fadeIn = [CABasicAnimation
                                   animationWithKeyPath:@"opacity"
    ];
    fadeIn.fromValue = @0;
    fadeIn.toValue   = @1;
    fadeIn.duration  = 1.5;
    
    // 移動アニメーションを作成
    CABasicAnimation *move = [CABasicAnimation
        animationWithKeyPath:@"position.y"
    ];
    move.duration = 2.0;
    move.byValue  = @(50.0f);
   
    // グループに突っ込んで
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = @[
        fadeIn,
        move,
    ];
    
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;
    
    // group内部のアニメーションのbeginTimeと、groupのdurationを設定
    [group setDurationToSequentially];
   
    // アニメーションを実行
    [redSquare.layer addAnimation:group forKey:@"AnimationSample"];
}

```

##実装例:ちょい複雑なアニメーション
次に、下記の様なアニメーションの実装例をご紹介します。

1. 正方形がフェードイン＆拡大を同時にしながら登場
2. 正方形が下に移動したあとさらに右に移動
3. 正方形がフェードアウト＆縮小しながら消える
 
### 正方形がフェードイン＆拡大を同時にしながら登場
まずは、正方形がフェードインしながら、拡大するというアニメーショングループを作成します。

さきほどは、setDurationToSequentiallyを叩くことによりアニメーションを順次再生するようbeginTimeとdurationを調整できることを説明しました。同時に2つのアニメーションを実行したい！って時はsetDurationToSameTimeSpawnというCAAnimationGroup用カテゴリのメソッドを叩くことでbeginTimeとdurationを良きに計らって調整してくれます。それでは以下、フェードイン＆拡大するアニメーションです。

```objectivec:ViewController.m
- (CAAnimationGroup *) appearAnimation {
    // フェードアニメーションを実装
    CABasicAnimation* fadeIn = [CABasicAnimation
        animationWithKeyPath:@"opacity"
    ];
    fadeIn.fromValue = @0;
    fadeIn.toValue   = @1;
    fadeIn.duration  = 1.5;
    
    // 拡大アニメーションを実装
    CABasicAnimation *zoomIn= [CABasicAnimation
        animationWithKeyPath:@"transform.scale"
    ];
    zoomIn.duration    = 1.5;
    zoomIn.fromValue = [NSNumber numberWithFloat:1.0];
    zoomIn.toValue   = [NSNumber numberWithFloat:2.0];
   
    // 出現アニメーションをグループにまとめる
    CAAnimationGroup *appear = [CAAnimationGroup animation];
    appear.animations = @[
        fadeIn,
        zoomIn,
    ];
    // 出現アニメーションは、２つのアニメーションを同時再生
    [appear setDurationToSameTimeSpawn];
    
    return appear;
}
```

### 正方形が下に移動したあとさらに右に移動
これは、最初の例と同じように移動するアニメーションが入ったグループを作成し、setDurationToSequentiallyを叩くことで実装できます。

```objectivec:ViewController.m
- (CAAnimationGroup *) moveAnimation {
    // 下に移動
    CABasicAnimation *moveToBottom = [CABasicAnimation
        animationWithKeyPath:@"position.y"
    ];
    moveToBottom.duration = 1.5;
    moveToBottom.byValue  = @(100.0f);

    // 右に移動
    CABasicAnimation *moveToRight = [CABasicAnimation
        animationWithKeyPath:@"position.x"
    ];
    moveToRight.duration = 1.5;
    moveToRight.byValue  = @(100.0f);
    
    // 移動アニメーションをグループにまとめる
    CAAnimationGroup *move = [CAAnimationGroup animation];
    move.animations = @[
        moveToBottom,
        moveToRight,
    ];
    // 移動アニメーションは、2つのアニメーションを順次再生
    [move setDurationToSequentially];
    
    return move;
}
```
### 正方形がフェードアウト＆縮小しながら消える
これも、正方形の出現時と逆の処理なので特に新しいことをする必要はないです。

```objectivec:ViewController.m
- (CAAnimationGroup *) disappearAnimation {
    // フェードアウトアニメーションを実装
    CABasicAnimation* fadeOut = [CABasicAnimation
        animationWithKeyPath:@"opacity"
    ];
    fadeOut.fromValue = @0;
    fadeOut.toValue   = @1;
    fadeOut.duration  = 1.5;
    
    // 縮小アニメーションを実装
    CABasicAnimation *zoomOut = [CABasicAnimation
        animationWithKeyPath:@"transform.scale"
    ];
    zoomOut.duration  = 1.5;
    zoomOut.fromValue = [NSNumber numberWithFloat:2.0];
    zoomOut.toValue   = [NSNumber numberWithFloat:0.0];
   
    // サヨナラアニメーションをグループにまとめる
    CAAnimationGroup *disappear = [CAAnimationGroup animation];
    disappear.animations = @[
        fadeOut,
        zoomOut,
    ];
    // サヨナラアニメーションは、2つのアニメーションを同時再生
    [disappear setDurationToSameTimeSpawn];
    
    return disappear;
}
```

### アニメーショングループを束ねる
下記の様に、これまでに作成したアニメーショングループをさらに一つに束ねてsetDurationToSequentiallyを叩くことにより、それぞれのグループのアニメーションを順次再生することが出来ます。

```objectivec:ViewController.m
- (void) _playAnimation {
    // アニメーションで動かすためのViewを作成
    UIView *redSquare = [[UIView alloc]
         initWithFrame:
         CGRectMake(100,300,50,50)
    ];
    redSquare.backgroundColor = [UIColor redColor];
    [self.view addSubview:redSquare];
   
    // ３つのアニメーションをさらに一つのグループにまとめる
    CAAnimationGroup *animations = [CAAnimationGroup animation];
    animations.animations = @[
        [self appearAnimation],
        [self moveAnimation],
        [self disappearAnimation]
    ];
    // 3のアニメーションを順次再生
    [animations setDurationToSequentially];
   
    // アニメーションを実行
    [redSquare.layer addAnimation:animations forKey:@"AnimationSample"];
}
```

----------
完成！この様に、グループにまとめてアニメーションを管理することで、ある程度複雑なアニメーションをシーケンシャルに再生したい、という場合でも割りかしシンプルに管理することが出来ます。こちらからは、以上です。良きアニメーション実装ライフを！