LoginSignup
1
1

More than 1 year has passed since last update.

StackViewの挙動を確認した話

Last updated at Posted at 2021-05-05

準備

色と大きさを指定できる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が持っていた高さの制約よりも優先される。

スクリーンショット 2021-05-05 16.49.17.jpg

leading/top

axisがhorizontalのt時はtop、verticalの時はleadingが適切であるよう(Storyboardだと表示されなくなる)だが、どちらでも動く。中身のUIViewの上の辺(topAnchor)がx,y座標の小さい方の上の辺に揃う。

スクリーンショット 2021-05-05 15.29.06.jpg

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![スクリーンショット 2021-05-05 15.19.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/68321e3f-f289-6d9d-14a8-55a1761c3919.jpeg)
ArrangedSubview(view)
        }
    }

}
PlaygroundPage.current.liveView = View()

スクリーンショット 2021-05-05 15.15.27.jpg

center

StackViewの鉛直中央(centerYAnchor)に中身の鉛直中央が揃うように配置される。

スクリーンショット 2021-05-04 11.15.34.jpg

trailing/bottom

leading/topの逆ver。

スクリーンショット 2021-05-05 15.43.04.jpg

lastBaseLine

StackViewにUILabelが含まれている時、その最後の行に高さが揃う。

スクリーンショット 2021-05-05 15.19.14.jpg

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
    }

fill

StackViewのaxis方向の幅(horizontalであればwidth、verticalであればheight)がいっぱいになるまでUIViewを広げる。中身の辺の幅が足りない時にどのUIViewの幅が伸ばされるのかということについては調査中であるが、UILayoutPriorityあたりが絡んでいそう。画像では最初に追加したredViewのwidthが伸ばされている。

スクリーンショット 2021-05-05 16.44.25.jpg

なお、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を均等に広げる。

スクリーンショット 2021-05-05 16.44.08.jpg

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))

スクリーンショット 2021-05-05 16.52.09.jpg

equalSpacing

中身のUIViewの間隔が等しくなるように配置する。

スクリーンショット 2021-05-05 16.59.04.jpg

equalCentering

中身のUIViewの中心の間隔が等しくなるように配置される。
スクリーンショット 2021-05-05 16.59.24.jpg

ソースコード

コピペで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()

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1