Objective-C
iOS
UIViewController
カスタム

カスタムContainer View Controllerを作る

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