Thursday, July 14, 2011

iOS autoresizing UIView TIPS

2011-11-26: 根本的に理解が足りない気がしてきた。 autoresizingMask や layoutSubviews を使えば済む場合が多いはずなので整理ができたらまとめます。
2011-10-04: view controller を window に直接 addSubview するのでなく UINavigationController や UITabBarController 配下にするばあいは viewWillAppear でも orientation は反映されているようなので、あとで調べて情報を更新したい
2011-08-05: orientation が landscape の場合には - didRotateFromInterfaceOrientation: まで待つ必要があったので修正

InterfaceBuilder を使わずにコードで iOS の view 階層を作成して画面サイズや orientation にあわせて autoresize させるにはいろいろと注意が必要になるのですが、イマイチ情報が無かったのでわかったことを書いておきます。

ポイントとしては、 Apple のドキュメント "View Controller Programming Guide for iPhone OS" (rev. 2010-05-03) の "Custom View Controllers" だと、コードで view 階層を作成するには loadView でやれみたいに読めるのですが、 loadView や viewDidLoad のタイミングでは autoresize はまだ反映されず、 viewWillAppear のタイミングで反映されるようです。
さらに、 orientation が landscape (ヨコ) の場合には viewWillAppear ではダメで didRotateFromInterfaceOrientation: まで待たないとダメなようです。

ちなみに、 orientation が viewController.view に反映されているかどうかは、 view.transform に rotation の値が入ってるかどうかで分かるようです。
<UIView: 0x5b59d80; frame = (20 0; 748 1024); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x5b532a0>>


従って、画面のサイズ(iPhone or iPad)、 navigation bar などの表示の有無 によって autoresize される view の frame や bounds に反映されるのは viewWillAppear のタイミングなので、例えば、それらの autoresize される view の subviews として view をグリッド状に並べたりするのは viewWillAppear でやれってことになります (orientation が portrait でないときは didRotateFromInterfaceOrientation)。

ちなみに、 loadView で生成して addSubview する view をおまかせで画面に fit させる場合、
initWithFrame する必要は無く、 frame は (0 0; 0 0) で OKのようです。
- (void)laodView
{
    UIView *rootView = [[[UIView alloc] init] autorelease];
    rootView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.view = rootView;
}


逆に fit させる大きさ(この場合、navigation bar などを除いたサイズ) より大きい frame を設定してしまうと autoresize はうまくゆかないようです。

また、 [super loadView] で自動生成される view は [[UIView alloc] init] した view とは違い、そのタイミングでの (orientation の反映されていない) frame になっていたり、プロパティの値が違うのか、期待どおりの動きにするのが面倒な感じなので自前で [[UIView alloc] init] したほうがよさそうです。