準備
色と大きさを指定できるUIViewを作成
class ColorView: UIView {
var size: CGSize
init(color: UIColor, size: CGSize) {
self.size = size
super.init(frame: CGRect())
backgroundColor = color
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return CGSize(width: size.width, height: size.height)
}
}
表示するViewを作成。この中で先程の`ColorView`を使っている。
class View: UIView {
let stackView = UIStackView()
let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100))
let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100))
let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100))
let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100))
let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100))
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setupStackView()
setupColorViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupStackView() {
stackView.backgroundColor = .gray
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fillProportionally
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.widthAnchor.constraint(equalTo: self.widthAnchor)
])
}
private func setupColorViews() {
let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView]
colorViews.forEach { view in
stackView.addArrangedSubview(view)
}
}
}
axis
これは理解しやすい。積み上げる方向が水平方向か鉛直方向か指定できる。
UIKit.UIStackViewでの定義
extension NSLayoutConstraint {
public enum Axis : Int {
case horizontal = 0
case vertical = 1
}
}
alignment
alignmentは名詞で一列、整列線といった意味。方向を定めた後にどこに並ぶのかを決める。
UIKit.UIStackViewでの定義
public enum Alignment : Int {
/* Align the leading and trailing edges of vertically stacked items
or the top and bottom edges of horizontally stacked items tightly to the container.
*/
case fill = 0
/* Align the leading edges of vertically stacked items
or the top edges of horizontally stacked items tightly to the relevant edge
of the container
*/
case leading = 1
public static var top: UIStackView.Alignment { get }
case firstBaseline = 2 // Valid for horizontal axis only
/* Center the items in a vertical stack horizontally
or the items in a horizontal stack vertically
*/
case center = 3
/* Align the trailing edges of vertically stacked items
or the bottom edges of horizontally stacked items tightly to the relevant
edge of the container
*/
case trailing = 4
public static var bottom: UIStackView.Alignment { get }
case lastBaseline = 5 // Valid for horizontal axis only
}
fill
fillは高さが揃うようにaxisと垂直方向の辺の長さを広げる。
中身のViewが持っていた高さの制約よりも優先される。
leading/top
axisがhorizontalのt時はtop、verticalの時はleadingが適切であるよう(Storyboardだと表示されなくなる)だが、どちらでも動く。中身のUIViewの上の辺(topAnchor
)がx,y座標の小さい方の上の辺に揃う。
firstBaseLine
StackViewにUILabelが含まれている時、その1行目に高さが揃う。
なお、firstBaseLineとlastBaseLineで用いているコードは他のものと異なっている。
import PlaygroundSupport
import UIKit
class ColorView: UIView {
let size: CGSize
init(color: UIColor, size: CGSize) {
self.size = size
super.init(frame: CGRect())
backgroundColor = color
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return CGSize(width: size.width, height: size.height)
}
}
class View: UIView {
let label = UILabel()
let redView = ColorView(color: .red, size: CGSize(width: 100, height: 20))
let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 40))
let yelloView = ColorView(color: .yellow, size: CGSize(width: 100, height: 60))
let stackView = UIStackView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
addSubViewsConstrains()
setupStackView()
setupLabel()
}
private func addSubViewsConstrains() {
//StackView
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.9),
stackView.heightAnchor.constraint(equalToConstant: 500)
])
//Label
addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.centerYAnchor.constraint(equalTo: stackView.centerYAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupLabel() {
label.backgroundColor = .white
label.numberOfLines = 5
label.text = """
first BaseLine
second BaseLine
third BaseLine
4th BaseLine
last BaseLine
"""
}
private func setupStackView() {
stackView.backgroundColor = .gray
stackView.axis = .horizontal
stackView.alignment = .lastBaseline
stackView.distribution = .fillEqually
let views: [UIView] = [redView,label,orangeView,yelloView]
views.forEach { view in
stackView.add
ArrangedSubview(view)
}
}
}
PlaygroundPage.current.liveView = View()
center
StackViewの鉛直中央(centerYAnchor
)に中身の鉛直中央が揃うように配置される。
trailing/bottom
leading/topの逆ver。
lastBaseLine
StackViewにUILabelが含まれている時、その最後の行に高さが揃う。
destribution
destributionは名詞で配分や散布という意味。axisで定めた方向に対して、中身のUIViewをどのように広げるのかを定めるのがdestributionであるといえる。
UIKit.UIStackViewでの定義
public enum Distribution : Int {
case fill = 0
case fillEqually = 1
case fillProportionally = 2
case equalSpacing = 3
case equalCentering = 4
}
StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまでUIViewを広げる。中身の辺の幅が足りない時にどのUIViewの幅が伸ばされるのかということについては調査中であるが、UILayoutPriorityあたりが絡んでいそう。画像では最初に追加したredView
のwidthが伸ばされている。
なお、destributionで説明する画像の中でalignmentはcenterにしてある。
import PlaygroundSupport
import UIKit
class ColorView: UIView {
var size: CGSize
init(color: UIColor, size: CGSize) {
self.size = size
super.init(frame: CGRect())
backgroundColor = color
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return CGSize(width: size.width, height: size.height)
}
}
class View: UIView {
let stackView = UIStackView()
let redView = ColorView(color: .red, size: CGSize(width: 100, height: 100))
let orangeView = ColorView(color: .orange, size: CGSize(width: 100, height: 100))
let yellowView = ColorView(color: .yellow, size: CGSize(width: 100, height: 100))
let greenView = ColorView(color: .green, size: CGSize(width: 100, height: 100))
let blueView = ColorView(color: .blue, size: CGSize(width: 100, height: 100))
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .gray
setupStackView()
setupColorViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupStackView() {
stackView.backgroundColor = .white
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.widthAnchor.constraint(equalTo: self.widthAnchor)
])
}
private func setupColorViews() {
let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView]
colorViews.forEach { view in
stackView.addArrangedSubview(view)
}
}
}
PlaygroundPage.current.liveView = View()
fillEqualy
StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまで中身のViewを均等に広げる。
fillProportionally
中身のUIViewの幅の和がStackViewの幅に満たない時、中身のUIViewの幅の比をもとに各UIViewを広げる。
これは各UIViewのwidthを1,2,3,4,5にしているので、stackViewのwidthを1:2:3:4:5に分割するように広げられる。
let redView = ColorView(color: .red, size: CGSize(width: 1, height: 100))
let orangeView = ColorView(color: .orange, size: CGSize(width: 2, height: 100))
let yellowView = ColorView(color: .yellow, size: CGSize(width: 3, height: 100))
let greenView = ColorView(color: .green, size: CGSize(width: 4, height: 100))
let blueView = ColorView(color: .blue, size: CGSize(width: 5, height: 100))
equalSpacing
中身のUIViewの間隔が等しくなるように配置する。
equalCentering
中身のUIViewの中心の間隔が等しくなるように配置される。
ソースコード
コピペでPlaygroundsで動きます。
import PlaygroundSupport
import UIKit
class ColorView: UIView {
var size: CGSize
init(color: UIColor, size: CGSize) {
self.size = size
super.init(frame: CGRect())
backgroundColor = color
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return CGSize(width: size.width, height: size.height)
}
}
class View: UIView {
let stackView = UIStackView()
let redView = ColorView(color: .red, size: CGSize(width: 60, height: 100))
let orangeView = ColorView(color: .orange, size: CGSize(width: 90, height: 100))
let yellowView = ColorView(color: .yellow, size: CGSize(width: 120, height: 100))
let greenView = ColorView(color: .green, size: CGSize(width: 150, height: 100))
let blueView = ColorView(color: .blue, size: CGSize(width: 180, height: 100))
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .gray
setupStackView()
setupColorViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupStackView() {
stackView.backgroundColor = .white
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .equalCentering
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.widthAnchor.constraint(equalTo: self.widthAnchor)
])
}
private func setupColorViews() {
let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView]
colorViews.forEach { view in
stackView.addArrangedSubview(view)
}
}
}
PlaygroundPage.current.liveView = View()