2012年8月18日土曜日

コンテナUIViewController

iOS4まではコンテナUIViewControllerは、用意されたNavigtionやTab、Splitを利用するものだったが、iOS5ではユーザーの手でカスタマイズされたコンテナUIViewControllerを作成できるようになった。

利用するパターンは、例えば次のような場合のときである。

  • NavigationやTabでは表現できない階層構造を表現したいとき
  • MasterDetailでは作成できない3ペインのアプリを作りたいとき
  • xibで生成した複数種類のコンポーネントを動的に切り替えたいとき
iOS4以前でも一画面内に複数のカスタムコンテンツUIViewControllerのviewを混在させること自体はできたが、UIViewControllerを階層化できるようになったことで、トランジションエフェクト付きでビューの遷移を行えたり、画面回転イベントなどを子のUIViewControllerに通知できるなど、利便性が大きく改善されている。

---

使い方は、UIViewControllerのクラスリファレンスより、「Implementing a Container View Controller」を読むのが手っ取り早い。

日本語であれば、UIViewControllerのコンテナ機能が、スライド資料だけどサンプルコードも付いているので、具体的な使い方が分かると思う。

英語資料でもよければ、Writing high-quality view controller containersがもっとも情報量が多い。

---

UIViewControllerをコンテナUIViewControllerにするために、特別に何かを行う必要はない。

iOS5以降では、最初からすべてのUIViewControllerはコンテンツとしても、コンテナとしても、あるいはその両方としても振舞うことができる。

---

ビューコントローラーを子要素にするためには、addChildViewControllerを使用する。

あくまでビューコントローラー間に階層が生まれるだけなので、実際に画面にビューを表示するためには、別途addSubviewでビュー間に階層を作る必要がある。

これにより、親のビューコントローラーのNSArrayプロパティ、ChildViewControllersに子ビューコントローラーが格納され、子ビューコントローラーのUIViewControllerプロパティ、parentViewControllerに親のビューコントローラーが格納される。

※parentViewControllerは、iOS4まではモーダル表示元のUIViewControllerが格納されていたプロパティなので混乱に注意。

逆に子要素のビューコントローラーを取り除くには、子のビューコントローラからremoveFromParentViewControllerを呼ぶ。

---

子ビューコントローラ間で表示を切り替えるために、専用のトランジションメソッドが用意されている。

transitionFromViewController:toViewController:duration:options:animations:completion:

fromとtoに子ビューコントローラを指定する。

  • fromのビューコントローラが保持するビューにremoveFromSuperviewが飛ぶ。
  • 変わりにtoのビューコントローラが保持するビューがaddSubviewされる。
    • fromを保持していたビューに対して、addSubviewが行われる。
  • addChildViewController、removeFromParentViewControllerは呼ばれない。
    • fromが必要なくなった場合は、completionブロックでremoveFromParentViewControllerを呼んで解放する。
  • 書く必要のないaddSubviewを書くと、警告が出るので注意。
また、durationでアニメーション時間を、optionsでいくつかのトランジションエフェクトを選択できる。

組み込みのトランジションエフェクトが気に入らなければ、animationsにカスタムアニメーションを記述できる。

記述方法はblock構文によるUIViewアニメーションと同じ。self.viewのプロパティを操作するのではなく、ここではfromとtoのviewのプロパティを操作する。

---

基本的な使い方としては、

  1. [self.childViewControllers lastObject]で直前のビューコントローラの参照を取得する
  2. [self addChildViewController:newViewController]で新しいビューコントローラを子に加える
  3. それぞれをFrom、Toとして遷移アニメーションを実行
  4. completionブロックでFromのビューコントローラを解放

といった感じ。

例えば複数ペインの画面構成など、常時複数のビューコントローラーを子として保持する場合には、lastObjectは使えないので、isKindOfClassでクラス判定を行うか、プロパティとして保持しておくのがいいと思う。

---

注意すべき点

コンテナUIViewControllerを実装する場合、
  • addChildViewController:の直後にdidMoveToParentVieController:
  • removeFromParentViewController:の直前にwillMoveToParentViewController:
を、それぞれ呼び出さなければならない(must)。
  • addChildViewController:の直前にwillMoveToParentViewController:
  • removeFromParentViewController:の直後にdidMoveToParentViewController:
は、デフォルトでは自動的に呼ばれるので、何もしなくて良い。

ただしautomaticallyForwardAppearanceAndRotationMethodsToChildViewControllersをオーバーライドして、NOを返すと上記のメソッドも自動的には呼ばれなくなるので、自分の手で明示的に呼び出す必要がある。

画面転回によるイベントを子ビューコントローラーに自動転送したくない状況が存在する場合を除いては、デフォルト設定のままで問題ないと思う。たぶん。

0 件のコメント:

コメントを投稿