遅ればせながら UIStackView 入門

  • 39
    いいね
  • 0
    コメント

仕事で関わっているプロジェクトはiOS8のサポートを打ち切ったこともあり、2016年の師走に今更ながらUIStackViewについて調べてみました。

UIStackViewとは

  • AutoLayoutのラッパークラス
  • iOS9以上で利用可能
  • 煩雑な制約を使わずに複数のViewを水平/垂直方向にレイアウト可能
  • デバイスの回転、スクリーンサイズなどに動的に対応可能
Horizontal Vertical
axis-horizontal.png axis-vertical.png

レイアウトに関わるプロパティ

UIStackViewは主に次の4つのプロパティを使って設定します。

  • axis alignment distribution spacing

axis

var axis: UILayoutConstraintAxis

サブビューを X軸方向に並べる場合は horizontal、 Y軸方向に並べる場合は vertical を指定します。

alignment

var alignment: UIStackViewAlignment

axis と垂直軸方向におけるサブビューの配置方法を指定します。

axishorizontal の場合、top center bottom fill が指定可能です。
また、vertical の場合、 leading center trailing fill が指定可能です。
fill 以外に関しては、各サブビューは Intrinsic Content Size に基づいてサイズが決定されます。
fill では、axisとの垂直方向の領域を埋めるようにして各ビューがリサイズされます。

axis: .horizontal の場合

alignment-horizontal.png

axis: .vertical の場合

alignment-vertical.png

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 の場合

distribution-horizontal.png

axis: .vertical の場合

distribution-vertical.png

spacing

var spacing: CGFloat

サブビュー間の余白を指定します。 distributionequalSpacingequalCentering の場合、無視されます。

サブビューの管理方法

UIStackViewはUIViewのサブクラスのため subviews プロパティを持ちますが、実際のレイアウトは arrangedSubviews プロパティのサブビューが対象となります。
arrangedSubviews への追加は addArrangedSubview() を利用します。arrangedSubviewssubviews のサブセットのため、このメソッド経由で追加されたビューは subviews に追加されます。
なお、 ビューを addSubview()subviews に追加しただけでは arrangedSubviews には追加されず、UIStackViewでのレイアウト制御の対象として扱われないので注意が必要です。

UIStackViewの作り方

UIStackViewを使って次のようなビュー構成を作る場合のサンプルです。
(※Storyboardの場合の例は割愛)

make-programatically.png

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 プロパティに対応しています。
isHiddenfalsetrue に変えるだけでStackView上から非表示にでき、かつ他のサブビューが再レイアウトされます。

所感

簡単で使いやすいとは昨年から見聞きしてましたが、全くその通りでした。
大抵のレイアウトはUIStackViewで実装できそうな気がします。
iOS8のサポートを打ち切ったプロジェクトでは使わない理由はありませんね。


Credit(画像アイコン)