Help us understand the problem. What is going on with this article?

カスタムContainer View Controllerを作る

More than 5 years have passed since last update.

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にあげてみました。プログラミングガイドをほぼそのまま使ったものですが。

edo_m18
現在はUnity ARエンジニア。 主にARのコンテンツ制作をしています。 最近は機械学習にも興味が出て勉強中です。 Unityに関するブログは別で書いています↓ https://edom18.hateblo.jp/
http://edom18.hateblo.jp/
unity-game-dev-guild
趣味・仕事問わずUnityでゲームを作っている開発者のみで構成されるオンラインコミュニティです。Unityでゲームを開発・運用するにあたって必要なあらゆる知見を共有することを目的とします。
https://unity-game-dev-guild.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away