StackViewとAutolayoutあれこれ


Xcode7から追加されたUIStacViewを使ってみます。
イメージとしてはAndroidにおけるLinearLayoutに近いんじゃないでしょうか。
StackViewはiOS9以降でしか対応していないのでiOS8にも対応する必要があるアプリでは使えません。

以下は公式のAutoLayoutGuideを参考にしています。

簡単なStackView

まずは簡単に使ってみます。

Storyboard上で"Vertical Stack View"を選択してViewControllerに配置します。
StackView内は特にAutoLayoutの設定は必要ありませんが、StackView自体には必要です。
ここでは親Viewいっぱいに広げておきます。
次にStackViewのAttributeInspectorからSpacingを8に設定します。
これはStackView内に配置したコントロール間の間隔になります。
今の段階での設定内容は以下のようになっています。

この中へLabel、ImageView、Buttonを順番に配置していきます。
自動的に上下方向に配置されていきます。

このままだとボタンが大きく広がってしまっていますが、設定する画像サイズに関わらずStackView内の
空き領域全体を占めていて欲しいのでサイズインスペクターから"Content Hugging Priority"のVerticalを249に、"Content Compression Resistance Priority"のVertialを749にします。
この制約はそれぞれ「推奨サイズより大きくなりくさ」「推奨サイズより小さくなりにくさ」を示します。
この値を小さくするということはつまり推奨サイズより大きくなったり小さくなったりしやすくなる、ということです。
ImageViewは与えられた画像により「推奨サイズ」が変化しますが、これにより小さな画像がImageViewに設定されれば「推奨サイズより大きく」なることで空きスペースを埋め、大きな画像が設定されれば「推奨サイズより小さく」なることで空きスペースに収まるようになります。

複雑なStackView

公式のAutoLayoutGuideを参考にもう少し複雑なレイアウトで試してみます。
まずテキトーにレイアウトイメージに沿ってコントロールを配置します。

この段階ではまだ何も設定していません。
まずはラベルとテキストフィールドが横に並んでいる部分からStackViewに突っ込んでいきます。
ラベルとテキストビューを1対選択してメニューから[Editor]-[Embed In]-[Stack View]と選択します。
すると選択した2つのコントロールがStackViewに格納されました。
何だか窮屈になっていますが気にしない。
他の2つのラベルとテキストフィールドのペアも同様にセットします。
この時点ではこんな感じ。

次にこの3つのStackViewを選択して[Editor]-[Embed In]-[Stack View]と選択します。
この3つまとまったStackViewと隣のImageViewを選択して同様の操作でStackViewに格納します。
さらに下側にある3つのボタンを選択して同様にStackViewに格納します。

この段階では大丈夫か?という感じがしなくもないですが。
次に上側のStackView、テキストビュー、下側のStackViewを選択してStackViewに格納します。
一番外側のStackViewを画面いっぱいになるように制約を追加します。
StackViewの内側は設定不要です。

多少、まともになってきました。
次にImageViewのサイズを120x120くらい(テキトーでいいです)にしてAspectRatio制約を追加します。
この状態でUpdate FramesをしてしまうとImageViewが大きく広がってしまいます。
右側のテキストビューを大きくしたいので右側に位置する3つのテキストビューの"Content Hugging Priority"を「48」にします。

この「48」という数字は公式テキストから引っ張ってきたのですが正直、どこから出てきた数字なのかよく分かりません。
「50」でも同じ見た目になります。「51」だとImageViewが広がった状態になります。
どういう計算式なのか、分かりません。
ごめんなさい。教えてください。

で、この状態だと上の段が広がりすぎているので中段のテキストビューが広がるようにします。
テキストビューの"Content Hugging Priority"を249に下げて「大きくなりやすく」します。
これでテキストビューが広がります。
最下段のボタンが左端のものが広がって右側に2つ寄ってしまっているので、こちらを修正します。
一番下のStackViewのアトリビュートインスペクターからDistributionを「Fill Equally」にします。
こうすると3つのボタンが均等に並びます。
そして最後に各StackViewの"Spacing"を8にします。

これで完成。

まとめ

Content Hugging PriorityとContent Compression Resistance Priorityを直接編集する機会というのは、InterfaceBuilderがうまいことやってくれることもあってこれまであまり無かったのですが、StackViewを使うと意識しなければならないケースが多くなりそうです。
計算方法でちょっと分からない部分がありますが、複雑なレイアウトが必要なケースでiOS8を捨てて良いなら
StackViewを使うことも検討してみたいです。