UIStackViewを使ってViewを比で分割したい


やりたいこと


↑のようなイメージでViewを比で分割したい

環境

Xcode 12.5.1
Swift 5.4.2

UIStackViewの準備

import UIKit

class ViewController: UIViewController {

    let topView: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        return view
    }()

    let middleView: UIView = {
        let view = UIView()
        view.backgroundColor = .yellow
        return view
    }()

    let bottomView: UIView = {
        let view = UIView()
        view.backgroundColor = .black
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        //UIStackViewの設定
        let stack = UIStackView(arrangedSubviews: [topView, middleView, bottomView])
        stack.axis = .vertical
        stack.alignment = .fill
        stack.distribution = .fillEqually
        stack.translatesAutoresizingMaskIntoConstraints = false

        //viewに追加
        view.addSubview(stack)

        //制約を追加
        [
            stack.topAnchor.constraint(equalTo: view.topAnchor),
            stack.leftAnchor.constraint(equalTo: view.leftAnchor),
            stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stack.rightAnchor.constraint(equalTo: view.rightAnchor)
        ].forEach { $0.isActive = true }

    }

}


↑実行するとこんな感じ,これを比率で分けたい

それぞれのViewの高さの制約をいじってみた

        [
            stack.topAnchor.constraint(equalTo: view.topAnchor),
            stack.leftAnchor.constraint(equalTo: view.leftAnchor),
            stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stack.rightAnchor.constraint(equalTo: view.rightAnchor),
            // 高さの制約を追加
            topView.heightAnchor.constraint(equalTo: stack.heightAnchor, multiplier: 1/6),
            middleView.heightAnchor.constraint(equalTo: stack.heightAnchor, multiplier: 2/6),
            bottomView.heightAnchor.constraint(equalTo: stack.heightAnchor, multiplier: 3/6)
        ].forEach { $0.isActive = true }

高さの制約を追加し,stackViewのdistributionをfillEquallyからfillに変更
上のコードではmultiplierパラメータのところで高さの比を1:2:3に設定
※当然ながらmultipilerパラメータの値の合計が1になっていないと制約が無視されて表示されます.

いい感じにできました.

最後に

extension UIStackView {
    func divide(by ratio: [CGFloat], baseHeight: NSLayoutDimension) {
        translatesAutoresizingMaskIntoConstraints = false
        for (view, element) in zip(arrangedSubviews, ratio) {
            view.heightAnchor.constraint(equalTo: baseHeight,
                                         multiplier: element/ratio.reduce(0, +)).isActive = true
        }
    }
}

関数にしてみました. 
もっと良い書き方あるよっていう方は教えていただけると幸いです!ありがとうございました!