仕事で関わっているプロジェクトはiOS8のサポートを打ち切ったこともあり、2016年の師走に今更ながらUIStackViewについて調べてみました。
UIStackViewとは
- AutoLayoutのラッパークラス
- iOS9以上で利用可能
- 煩雑な制約を使わずに複数のViewを水平/垂直方向にレイアウト可能
- デバイスの回転、スクリーンサイズなどに動的に対応可能
| Horizontal | Vertical |
|---|---|
![]() |
![]() |
レイアウトに関わるプロパティ
UIStackViewは主に次の4つのプロパティを使って設定します。
-
axisalignmentdistributionspacing
axis
var axis: UILayoutConstraintAxis
サブビューを X軸方向に並べる場合は horizontal、 Y軸方向に並べる場合は vertical を指定します。
alignment
var alignment: UIStackViewAlignment
axis と垂直軸方向におけるサブビューの配置方法を指定します。
axis が horizontal の場合、top center bottom fill が指定可能です。
また、vertical の場合、 leading center trailing fill が指定可能です。
fill 以外に関しては、各サブビューは Intrinsic Content Size に基づいてサイズが決定されます。
fill では、axisとの垂直方向の領域を埋めるようにして各ビューがリサイズされます。
axis: .horizontal の場合
axis: .vertical の場合
distribution
var distribution: UIStackViewDistribution
axis 方向におけるサブビューの配置方法を指定します。
fill fillEqually FillProportionally EqualSpacing EqualCentering が指定可能です。
-
fill-
hugging prioritycompression resistance priorityを元にサブビューをリサイズします。曖昧な場合、arrangedSubviews(後述) の順に基づいてサブビューをリサイズします。
-
-
fillEqually- 各ビューを同一幅で配置します。
-
fillProportionally- Intrinsic Content Size に応じて幅が決まる
- 各サブビューは係数(StackViewの幅 / 各サブビューの幅の総和 ?)をかけてリサイズされる
-
equalSpacing- Intrinsic Content Size に応じて幅が決まる
- サブビュー間のスペースを等間隔に配置
-
equalCentering- 各サブビューの中心を等間隔に配置
- サブビューの幅の総和がStackViewより大きい場合、幅はリサイズされる。`
axis: .horizontal の場合
axis: .vertical の場合
spacing
var spacing: CGFloat
サブビュー間の余白を指定します。 distribution が equalSpacing や equalCentering の場合、無視されます。
サブビューの管理方法
UIStackViewはUIViewのサブクラスのため subviews プロパティを持ちますが、実際のレイアウトは arrangedSubviews プロパティのサブビューが対象となります。
arrangedSubviews への追加は addArrangedSubview() を利用します。arrangedSubviews は subviews のサブセットのため、このメソッド経由で追加されたビューは subviews に追加されます。
なお、 ビューを addSubview() で subviews に追加しただけでは arrangedSubviews には追加されず、UIStackViewでのレイアウト制御の対象として扱われないので注意が必要です。
UIStackViewの作り方
UIStackViewを使って次のようなビュー構成を作る場合のサンプルです。
(※Storyboardの場合の例は割愛)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
view.backgroundColor = UIColor.white
/// Instantiate StackView and configure it
let stackView = UIStackView(frame: .zero)
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
/// Setup StackView's constraints to its superview
view.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
/// Add subviews
let switchh = UISwitch()
switchh.isOn = true
switchh.backgroundColor = UIColor.cyan
stackView.addArrangedSubview(switchh)
let label = UILabel()
label.backgroundColor = UIColor.magenta
label.text = "label"
stackView.addArrangedSubview(label)
let button = UIButton(type: .infoDark)
button.backgroundColor = UIColor.yellow
stackView.addArrangedSubview(button)
その他
サブビューの isHidden プロパティに対応しています。
isHidden を false → true に変えるだけでStackView上から非表示にでき、かつ他のサブビューが再レイアウトされます。
所感
簡単で使いやすいとは昨年から見聞きしてましたが、全くその通りでした。
大抵のレイアウトはUIStackViewで実装できそうな気がします。
iOS8のサポートを打ち切ったプロジェクトでは使わない理由はありませんね。

