仕事で関わっているプロジェクトはiOS8のサポートを打ち切ったこともあり、2016年の師走に今更ながらUIStackViewについて調べてみました。
UIStackViewとは
- AutoLayoutのラッパークラス
- iOS9以上で利用可能
- 煩雑な制約を使わずに複数のViewを水平/垂直方向にレイアウト可能
- デバイスの回転、スクリーンサイズなどに動的に対応可能
Horizontal | Vertical |
---|---|
レイアウトに関わるプロパティ
UIStackViewは主に次の4つのプロパティを使って設定します。
-
axis
alignment
distribution
spacing
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 priority
compression 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のサポートを打ち切ったプロジェクトでは使わない理由はありませんね。