2012年8月20日月曜日

画面回転時にビューを画面いっぱいに表示させたい

UIViewControllerのクラスリファレンスによれば、
 If a view controller is owned by a window object, it acts as the window’s root view controller. The view controller’s root view is added as a subview of the window and resized to fill the window. If the view controller is owned by another view controller, then the parent view controller determines when and how the child view controller’s contents are displayed.

もしビューコントローラーがwindowオブジェクトによって保持される場合、それはルートビューコントローラとして動作する。windowのサブビューにaddSubviewされたビューコントローラーのルートビューは、windowを満たすようにリサイズされる。

もしビューコントローラーが他のビューコントローラーに保持される場合、親のビューコントローラーはいつどうやって子のビューコントローラーのコンテンツが表示されるかを決定する。

とあります。

----

例えばxibをルートビューコントローラーとなるよう呼び出した場合、ポートレートモードでもランドスケープモードでも、オートサイジングが機能して全画面を満たすように表示されます。

ナビゲーションコントローラーやタブバーコントローラーの子とした場合も、使える領域をフルに使えるよう、自動的にビューのサイズが調整されます。

こういう場合は何も考えなくていいです。

----

問題はカスタムコンテナUIViewControllerを使った場合です。

カスタムコンテナUIViewControllerの子コンテンツビューコントローラーとして、xibを呼び出した場合、そのままでは画面をいっぱいに使ってくれるのは、xibで指定した画面モードだけです。

ポートレートモードで画面を作った場合、ランドスケープモードでは残念なことになります。

xibはあくまで設定した画面モードの見た目だけを保持しているだけなのです。windowを満たそうとするルートビューコントローラーの特性や、ナビゲーションやタブバーが内部でリサイズしてくれるために、「xibで作ったレイアウトは当然画面を回転させれば自動でリサイズされる」と考えてしまいますが、それは間違いです。

----

なぜナビゲーションコントローラーやタブバーコントローラーは、自動的に画面サイズ調整を行なってくれるのでしょう?

それはユーザーがナビゲーションバーやタブバーなど、インターフェース要素のために必要なマージンを計算する手間を省くためでしょう。コンテンツの表示に利用可能な領域を算出する機能を持つならば、その領域にあわせてリサイズする機能も併せ持つことは合理的です。

翻ってカスタムコンテナUIViewControllerについて考えると、カスタムコンテナが他にインターフェース要素やビューを持っていて、どう子ビューをリサイズして配置するべきなのかシステムに知る術はないので、プログラマ側で配置するコードを書く必要があるわけです。

---

androidだとWRAP_CONTENTやMATCH_PARENTみたいに自動で計算してくれるオプションがあるので、iOSは凄く不便だと思う場所ですね…。

androidはリサイズのためにビュー階層を複数回探索しているので、その分速度を犠牲にしてもいます。iOSでは親が子のビューのサイズを算出するような仕組みが存在しないので、テーブルビューやポップオーバーでは専用のメソッドを用いてサイズを伝えなければならず、この辺りの一貫性のなさもあまり好きにはなれません。

----

カスタムコンテナUIViewControllerが、適切に子ビューコントローラーに回転状態を伝播させていれば、willRotateToInterfaceOrientation:duration:メソッドおよび、didRotateFromInterfaceOrientation:メソッドが呼び出されます。

ここで各々の画面の向きに応じて、子ビューがどう配置されるのかを計算して、frameプロパティを設定すればよいのです。

或いはルートビューコントローラーとなる、コンテナUIViewControllerのビューに、オートサイジング設定を施した空のUIViewを保持させて、子ビューコントローラーはそのUIViewにaddSubviewすることでどうにかなるような気もします。未確認。

----

まとめ

  • ルートビューコントローラーは常にウインドウを満たすようにリサイズする特性を持つ。
  • ナビゲーションコントローラー・タブバーコントローラーは、ナビゲーションバーなどインターフェース要素を除いた使用可能な領域を算出し、それを満たすようにリサイズしてくれる。
  • 自身でカスタムUIViewControllerをコンテナ化する場合は、子ビューコントローラーのビューがどう配置されなければならないのか記述する必要がある。画面転回時に自動的にリサイズされないので注意。

0 件のコメント:

コメントを投稿