カスタムContainer View Controllerを作る

  • 274
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

AppleのiOS View Controllerプログラミングガイドは色々と参考になるものが多かったです。
まだ読んでない人はぜひ一度読んでみてください。設計する上でもとても有用な情報が載っています。

Container View Controller(コンテナ)

Container View Controllerは、コンテナとしての役割を担うViewControllerです。
すでにあるものとしてはNavigationViewControllerTabViewControllerがそれに当たります。
つまり、子となるViewControllerを内包するちょっと特殊なViewControllerです。

コンテナViewControllerの役割

コンテナというくらいなので、なにかしらのViewController郡をまとめる役割を持ちます。
NavigationViewControllerであればスタックとしてViewControllerを管理し、TabViewControllerであれば並列関係のViewContrllerを管理します。

そして必要であれば、コンテナViewController独自のビュー領域(ナビゲーションバーやタブバーみたいなもの)を用意して、それを利用して内包しているViewControllerを表示したり、ということもできます。

ただ、実装を見ると分かりますが、基本的にはUIViewControllerのサブクラスでしかないので、コンテナの中にコンテナが、という入れ子構造ももちろん実現することができます。

コンテナViewControllerの作成手順

コンテナ用のViewControllerを作る際は、通常のViewControllerとは少し考慮することが変わります。
手順としては、

  1. addChildViewController:メソッドでViewControllerの親子関係を作る
  2. ViewControllerのviewを自身のviewのsubviewに追加する
  3. ViewControllerのdidMoveToParentViewController:メソッドを、自身を引数にして呼ぶ

という流れです。

(2)のview追加に関しては、通常のUIViewなどを管理するのと同じです。
(1)と(3)が特殊な部分ですが、よく考えればむずかしくありません。

ViewControllerを内包するということは親子関係を作るということ。
そのためのメソッドがaddChildViewController:メソッドです。
(逆に削除するにはremoveFromParentViewControllerメソッドを子ViewController側で呼びます)
このあたりはviewに対するaddSubviewremoveFromSuperview)などと同じ理屈ですね。

ちなみにaddChildViewController:を呼び出すと、自動的に、引数に渡されたViewControllerのwillMoveToParentViewController:メソッドが呼び出され、これからコンテナViewControllerの子要素になることが通知されるようになっています。

逆に、removeFromParentViewControllerを呼ぶと、didMoveToParentViewController:が呼ばれます。

ライフサイクルはどうなる?

上記のメソッドを適切に呼び出すことで、普段使っているライフサイクル、viewDidLoadviewWillAppearなどのメソッドが順次呼び出されていきます。

サンプルコード

Appleのプログラミングガイドのサンプルコードをコメントをつけつつ掲載します。
(サンプルはすべて、コンテナViewControllerのメソッドです)

子のViewControllerを追加する

- (void)displayContentController:(UIViewController *)content
{
    // 自身のビューコントローラ階層に追加
    // 自動的に子ViewControllerの`willMoveToParentViewController:`メソッドが呼ばれる
    [self addChildViewController:content];

    // 子ViewControllerの`view`を自身の`view`階層に追加
    [self.view addSubview:content.view];

    // 子ViewControllerに追加が終わったことを通知する
    [content didMoveToParentViewController:self];
}

子のViewControllerを削除する

- (void)hideContentController:(UIViewController *)content
{
    // これから取り除かれようとしていることを通知する(引数が`nil`なことに注意)
    [content willMoveToParentViewController:nil];

    // 子ViewControllerの`view`を取り除く
    [content.view removeFromSuperview];

    // 子ViewControllerを取り除く
    // 自動的に`didMoveToParentViewController:`が呼ばれる
    [content removeFromParentViewController];
}

ViewControllerの入れ替わりをアニメーションさせる

NavigationViewControllerなどのように、ViewControllerの入れ替え時にアニメーションさせることもできます。

- (void)cycleFromViewController:(UIViewController *)oldC toViewController:(UIViewController *)newC
{
    // 古いViewControllerに取り除かれようとしていることを通知する
    [oldC willMoveToParentViewController:nil];

    CGFloat width  = self.view.bounds.size.width;
    CGFloat height = self.view.bounds.size.height;

    // アニメーションスタート位置を画面下部にする
    CGRect startFrame = CGRectMake(0, height, width, height);
    [self addChildViewController:newC];

    newC.view.frame = startFrame;
    CGRect endFrame = CGRectMake(0, 100, width, height);

    [self transitionFromViewController:oldC
                        toViewController:newC
                                 duration:0.25
                                  options:0
                               animations:^{
                                    newC.view.frame = oldC.view.frame;
                                    oldC.view.frame = endFrame;
                               }
                            completion:^(BOOL finished) {
                                [oldC removeFromParentViewController];
                                [newC didMoveToParentViewController:self];
                            }];
}

transitionFromViewController:toViewController:duration:options:animations:completion:メソッドを使うことによって入れ替わりのアニメーションを実装することができます。

animations:にblocksを渡すことで、どういうアニメーションをさせるか、ということができます。
これを使えば、ちょっと変わったアニメーションをもったコンテナViewControllerが作れそうですね。

今回のサンプルはgithubにあげてみました。プログラミングガイドをほぼそのまま使ったものですが。