1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

StackViewのfillProportionallyが思い通りに動かなかった話

Posted at

StackView.destributionわかったかも!

「StackViewのfillProportionallyちょっとわかったかも。要はStackViewの大きさと中身が揃ってない時中身の比を考慮して敷き詰めてくれるって感じね。Playgroundsでサンプル作って動かそ」

5つのUIViewを用意してwidthAnchorとheightAnchorをそれぞれを100pt×100ptにして、`addArangeSubView`して。。。
import PlaygroundSupport
import UIKit

class View: UIView {
    let stackView = UIStackView()
    let redView = UIView()
    let orangeView = UIView()
    let yellowView = UIView()
    let greenView = UIView()
    let blueView = UIView()
    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 = .white
        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() {
        redView.backgroundColor = .red
        orangeView.backgroundColor = .orange
        yellowView.backgroundColor = .yellow
        greenView.backgroundColor = .green
        blueView.backgroundColor = .blue
        let colorViews: [UIView] = [redView,orangeView,yellowView,greenView,blueView]
        colorViews.forEach { view in
            NSLayoutConstraint.activate([
                view.widthAnchor.constraint(equalToConstant: 100),
                view.heightAnchor.constraint(equalToConstant: 100)
            ])
            stackView.addArrangedSubview(view)
        }
    }
}

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

「あれ??大きさを考慮して敷き詰めるなら100:100:100:100:100 = 1:1:1:1:1だから等間隔に敷き詰められるんじゃないの?」

調べてみると

どうやらfillProportionallyが比の参考にするのはframeのsizeではなく、intrinsicContentSizeらしい。当たり前といえば当たり前なのかも。

「なるほど。じゃあintrinsicContentSizeを100pt×100ptにしてみよ」

色と大きさを指定できる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)
    }
}

これを水平方向の画面いっぱいに広がるStackViewに入れる

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 = .white
        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)
        }
    }
}

「これでできるはず...」

PlaygroundPage.current.liveView = View()

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

「おーできた」

fillProportionallyらしいコーディングをしてみる

「widthの実際の値をもとにした比に基づいてwidthが定まるのがfillProportionallyだった」

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

なら、これは1:2:3:4:5になるはず

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

「おー、なってるやん。fillPortionally完全に理解できたわ!」

ソースコード

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: 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))
    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 = .white
        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)
        }
    }
}

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?