HOME » Natsu note » iOS » [iOS 7] 簡単にできる画面遷移のカスタマイズ

[iOS 7] 簡単にできる画面遷移のカスタマイズ 2013/12/18/|iOS, ,

iOS 7から、画面遷移のアニメーションをカスタマイズできるようになりました。実装はシンプルで本当に簡単です。しかし、やたらと長い(しかもイマイチ統一感のない)プロトコルをたくさん使わなくてはならないため、何となく二の足を踏んでいる方も多いのではないでしょうか。

今回は、画面遷移のカスタマイズの基本中の基本を紹介します。

関連プロトコル

画面遷移の実装には多くのプロトコルを使用します。その中で、最低限必要となるのは以下の3つです。

  • UIViewControllerAnimatedTransitioning(アニメーションコントローラ)
  • UIViewControllerContextTransitioning(画面遷移コンテキスト)
  • UIViewControllerTransitioningDelegate (画面遷移デリゲート※)

(※)モーダルビュー表示の場合。ナビゲーションコントローラまたはタブバーコントローラの場合は、代わりにそれぞれUINavigationControllerDelegateまたはUITabBarControllerDelegateが必要。

これらはすべてプロトコルです。まず、画面遷移時に用いるアニメーションの実態をUIViewControllerAnimatedTransitioningを採用したクラスで実装します。このクラスをアニメーションコントローラと呼びます。

アニメーションを実装する際には、画面遷移に関する情報が必要となります。例えば、遷移元・遷移先のビューなどがこれに当たります。これらをまとめて管理しているのがUIViewControllerContextTransitioningを採用したオブジェクトで、画面遷移コンテキストと呼ばれます。画面遷移コンテキストはシステムが作成します。開発者が作成することはありません。

最後に、画面遷移時にアニメーションコントローラを指定する必要があります。そのために用いるのがUIViewControllerTransitioningDelegateで、これを画面遷移デリゲートと呼びます。主に遷移元のビューコントローラ(またはAppDelegateなど)でこのプロトコルを採用し、画面遷移時にアニメーションコントローラを指定します。

アニメーションコントローラの作成

登場人物が分かったところで、さっそくアニメーションコントローラを作成してみます。一般的にはNSObjectのサブクラスを作成し、UIViewControllerAnimatedTransitioningを採用すれば十分です(画面遷移に物理動作アニメーションを取り入れる際にはUIDynamicBehaviorのサブクラスを作成する方が簡単です)。

非常にシンプルな例として、フェードアニメーションによる画面遷移を紹介します(遷移元の画面がフェードアウトする、つまりalpha=0.0になる)。クラス名をFadeAnimationControllerとします。ヘッダーは以下のようになります。プロパティやメソッドの宣言は必要ありません。

@interface FadeAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end

続いて実装部分です。UIViewControllerAnimatedTransitioningの必須メソッドは2つです。1つ目は画面遷移時間を示すtransitionDuration:で、2つ目はアニメーションを実装するためのanimateTransition:です。いずれも引数に画面遷移コンテキストを取ります。

画面遷移時間を1秒とするためには、transitionDuration:を以下のように実装します。

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
    return 1.0;
}

続いてアニメーションの実態です。手順はコード内のコメントで示した通りです。また、各手順の詳細を後述します。

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    // 画面遷移コンテキストから遷移元、遷移先ビューコントローラの取得 --- (1)
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    // 画面遷移コンテキストからコンテナビューを取得 --- (2)
    UIView *containerView = [transitionContext containerView];
    
    // コンテナビュー上に遷移先ビューを追加 --- (3)
    [containerView insertSubview:toVC.view belowSubview:fromVC.view];
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                     animations:^{
                         // アニメーションを実行 --- (4)
                         fromVC.view.alpha = 0.0;
                     }
                     completion:^(BOOL finished){
                         // 画面遷移完了を通知 --- (5)
                         [transitionContext completeTransition:YES];
                     }];
}

アニメーションコントローラを用いたカスタム画面遷移のアニメーションは、コンテナビューと呼ばれるビュー上で繰り広げられます。そのため、まずはじめに引数であるtransitionContext(システムが作成する画面遷移コンテキスト)から遷移元、遷移先のビューコントローラとコンテナビューを取得します(手順1, 2)。ほとんどの画面遷移で、最初にこの処理が必要となります。

続いて、遷移先のビューをコンテナビュー上に追加します(手順3)。遷移元のビューはコンテナビューにすでに乗っているのですが、遷移先のビューをコンテナビューに追加するのは開発者の役目です。これを忘れると、画面遷移が終了したときにビューの状態がおかしくなります。

ここまででアニメーションを開始する準備が整いました。フェードではなく、ビューが移動するようなアニメーションでは、ここまでに初期frameの設定を済ませます。次はいよいよアニメーションです。ここではUIViewのブロックメソッドを使っていますが、他にはUIKit Dynamicsなんかも使用可能です。

画面遷移終了時の状態をanimationsブロックに記述することで、アニメーションを実現します(手順4)。最後に、画面遷移コンテキストを通じてシステムに画面遷移が完了したことを通知します(手順5)。この処理を忘れると、ビューコントローラの状態が不正となりますので注意してください。

アニメーションコントローラはこれで完成です。

アニメーションコントローラの指定

アニメーションコントローラを作っただけでは宝の持ち腐れです。画面遷移に独自のアニメーションを適用するためには、アニメーションコントローラを指定する必要があります。そのために使うのが、UIViewControllerTransitioningDelegateです。

ここでは例として、Xcodeの”Utility Application”テンプレートで作成されるアプリを用います。モーダルビューを閉じる際に、先ほど作成したアニメーションコントローラを使用した画面遷移を行うように変更していきます。

まず始めに、モーダルビューの表示元(遷移元ではない)であるMainViewControllerでUIViewControllerTransitioningDelegateを採用し、アニメーションコントローラを指定できるようにします。

@interface MainViewController () <UIViewControllerTransitioningDelegate>
@end

続いて、prepareForSegue:で以下のように画面遷移デリゲートを設定します。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showAlternate"]) {
        [[segue destinationViewController] setDelegate:self];
        [[segue destinationViewController] setTransitioningDelegate:self]; // 画面遷移デリゲートを指定
    }
}

最後に、画面遷移デリゲートのメソッドを実装します。今回は、モーダルビュー消去時の画面遷移アニメーションを指定するため、以下のメソッドを用います。ここで、先ほど作成したFadeAnimationControllerのオブジェクトを返せば、これがアニメーションコントローラとして使用されます。

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return [[FadeAnimationController alloc] init];
}

ビルドして実行してみてください。モーダルビューを閉じる際のアニメーションが変更されたはずです。

今回は、非常に単純なフェードアニメーションを例として取り上げましたが、animateTransition:の実装を変更することで他のアニメーションを適用することができます。アニメーション部分が独立しているため、カスタマイズするのも簡単です。

一見複雑に見える画面遷移のカスタマイズですが、ここで紹介したほんの数行のコードで簡単に実現できることを実感していただけたのならば幸いです。

さらにステップアップ・・・(宣伝)

画面遷移をカスタマイズできるのはモーダルビュー表示のみではありません。ナビゲーションコントローラやタブバーコントローラでの画面遷移もカスタマイズ可能です。

また、画面遷移に使えるアニメーションは、ここで紹介したようなシンプルなものからスナップショットやKey-frameアニメーションを用いたもの、さらにはUIKit Dynamicsによる物理アニメーションまで多岐に渡ります。

そして、ジェスチャー等に合わせた独自の画面遷移(インタラクティブ画面遷移と呼びます)も実装することができます。

これらの詳細については、12月20日発売の以下の書籍にて、詳しく書いてありますので是非参考にしてみてください。50ページ以上を割いてしっかりと解説してあります(Chapter 2: 画面遷移、Chapter 3-2 UIKit Dynamicsの応用)。

画面遷移のカスタマイズが複雑に感じてしまうのは、おそらくプロトコル名の長さと分かりにくさ、モジュール間の関連の理解しにくさが原因だと思います。本書では、その点を踏まえた丁寧な解説を心がけました。

サンプルコード

ここで紹介したサンプルコードはこちらにおいてあります。参考にしてください。
https://github.com/natsuko/FadeTransitionSample


なお、サンプルでは分かりやすいようにビューの色を変更してあります。それ以外はXcodeで作成されるテンプレートに、ここで紹介したコードを追加したのみです。