Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

iOSのAuto Layout

櫻です。
今回はiOS6.0で導入されたCocoa Auto Layoutの紹介です。

iOSのアプリで固定的な画面のレイアウトはInterfaceBuilderやUIViewのautoresizingMaskで楽に作成できますが、動的に画面のレイアウトを変更する必要がある場合はViewControllerのviewDidLoadやViewのlayoutSubviewsをオーバーライドして頑張ってるのではないでしょうか?


iOS6からはCocoa Auto Layoutというものが導入されています。
(https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Articles/Introduction.html#//apple_ref/doc/uid/TP40010853)
これはview間のレイアウトの制約を指定することで実行時に自動でviewの配置を行ってくれるものです。NSLayoutConstraintクラスのオブジェクトでこの制約を指定するのですが、プログラムからはVisual Format Languageと呼ばれるフォーマットのテキストでルールを指定する事もできます。
(今回はこちらがメインです)
たとえば、

[button1]-[button2]

という文字列で「button1の右に少し間隔をあけてbutton2を配置する」という指定が行えます。

実際のコードでは

UIView *view = ...;      // 親のview
UIButton *button1 = ...;
UIButton *button2 = ...; 
NSDictionary *views = NSDictionaryOfVariableBindings(button1, button2);
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[button1]-[button2]"
                                                             options:0
                                                             metrics:nil
                                                               views:views];

という様な指定を行います。

Interface Builderからも指定ができるようです。

サンプルコード

動作確認で作成したアプリのコードは↓にあります。
https://github.com/hatena-iti/ios-autolayoutsample

何が嬉しいか

Auto Layoutを利用するとview間の配置のルールを指定するだけなので、それぞれのviewの座標を自力で計算する必要がなくなります。
またUILabelなどは表示する内容に応じて自動でサイズの調整を行ってくれます。

この機能を利用すると、図の表のようなレイアウトを面倒な座標計算無しに作成する事ができます。

f:id:IntelligentTechnology:20130826201422p:plain

図の部分に指定してあるのは横方向の指定に

|-[key1(==key2,==key3)]-[value1,==value2,==value3]
|-[key2]-[value2]
|-[key3]-[value3]

縦方向の指定に

V:[key1(==key2,==key3,==value1)]-margin-[key2]-margin-[key3]
V:[value1(==value2,==value3)]-margin-[value2]-margin-[value3]

です。

これだけの指定でlabelの内容が長くなると自動でうまくレイアウトを調整してくれるようになります。

f:id:IntelligentTechnology:20130826201526p:plain

少し頑張れば、座標計算をすることなくDictionaryから表のようなものが作成できるようになります。
(この用途であれば素直にUITableViewを利用したほうがよさそうですが…)

アニメーション

Auto Layoutで作成したviewのアニメーションは以下の様に指定するようです。

UIView *view = ...;        // このview内でアニメーション
NSArray constraints = ...; // アニメーション後に不要なレイアウトの指定
NSArray newConstraints = ...; // アニメーション後に新たに必要になるレイアウトの指定
[view removeConstraints:constraints];
[view addConstraints:newConstraints];
[UIView animateWithDuration:0.5 animations^{
	[view layoutIfNeeded];
}];

このアニメーションを実行すると、下図のように灰色の部分が右にアニメーションします。

f:id:IntelligentTechnology:20130826201647p:plain

viewの中身によってレイアウトを変える

もっと複雑なレイアウト調整を動的に行うにはUIViewのlayoutSubviewsをオーバーライドする必要があるようです。
参考の動画の45分ごろから解説があります。

感想

使い始めは、指定が不足してviewが表示されない事もありましたが、慣れれば座標計算を自力で行うよりは楽ができるのではないかと思います。(ここは意見が割れそうですが)
ただ、ソース内のVisual Format Languageの部分が静的に検査できないため、実行時までtypoなどに気付けないのが難点かもしれません。Interface Builderではある程度検査してくれるようです。
また同様に、XCodeリファクタリング機能で変数名を変更した場合、Visual Format Language内は自動で変更されないのもちょっとつらいところです。