AppleのiOS View Controllerプログラミングガイドは色々と参考になるものが多かったです。
まだ読んでない人はぜひ一度読んでみてください。設計する上でもとても有用な情報が載っています。
##Container View Controller(コンテナ)
Container View Controllerは、コンテナとしての役割を担うViewControllerです。
すでにあるものとしてはNavigationViewController
やTabViewController
がそれに当たります。
つまり、子となるViewControllerを内包するちょっと特殊なViewControllerです。
###コンテナViewControllerの役割
コンテナというくらいなので、なにかしらのViewController群をまとめる役割を持ちます。
NavigationViewController
であればスタックとしてViewControllerを管理し、TabViewController
であれば並列関係のViewContrllerを管理します。
そして必要であれば、コンテナViewController独自のビュー領域(ナビゲーションバーやタブバーみたいなもの)を用意して、それを利用して内包しているViewControllerを表示したり、ということもできます。
ただ、実装を見ると分かりますが、基本的にはUIViewController
のサブクラスでしかないので、コンテナの中にコンテナが、という入れ子構造ももちろん実現することができます。
###コンテナViewControllerの作成手順
コンテナ用のViewControllerを作る際は、通常のViewControllerとは少し考慮することが変わります。
手順としては、
-
addChildViewController:
メソッドでViewControllerの親子関係を作る - ViewControllerの
view
を自身のview
のsubviewに追加する - ViewControllerの
didMoveToParentViewController:
メソッドを、自身を引数にして呼ぶ
という流れです。
(2)のview追加に関しては、通常のUIViewなどを管理するのと同じです。
(1)と(3)が特殊な部分ですが、よく考えればむずかしくありません。
ViewControllerを内包するということは親子関係を作るということ。
そのためのメソッドがaddChildViewController:
メソッドです。
(逆に削除するにはremoveFromParentViewController
メソッドを子ViewController側で呼びます)
このあたりはview
に対するaddSubview
(removeFromSuperview
)などと同じ理屈ですね。
ちなみにaddChildViewController:
を呼び出すと、自動的に、引数に渡されたViewControllerのwillMoveToParentViewController:
メソッドが呼び出され、これからコンテナViewControllerの子要素になることが通知されるようになっています。
逆に、removeFromParentViewController
を呼ぶと、didMoveToParentViewController:
が呼ばれます。
####ライフサイクルはどうなる?
上記のメソッドを適切に呼び出すことで、普段使っているライフサイクル、viewDidLoad
やviewWillAppear
などのメソッドが順次呼び出されていきます。
###サンプルコード
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にあげてみました。プログラミングガイドをほぼそのまま使ったものですが。