iOS UIStackView 项目来包装内容,但分发项目以填充整个可用空间 space

iOS UIStackView items to wrap content but have the items distributed to fill entire available space

我有一个具有不同高度项目的 UIStackView。 项目的高度由它们的内容决定,我不想用约束来强制它。

我希望项目的间距始终占据整个可用屏幕。

我需要设置哪些设置才能使其正常工作?

这是一个非常简单的例子...

带有 3 个多行标签的垂直堆栈视图。每次我们点击任何地方,标签将是 re-filled 1 到 8 行文本,显示排列的子视图将确定自己的高度:

红色虚线矩形显示堆栈视图的框架。

示例代码如下:

class LenaBruViewController: UIViewController {
    
    let stack: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.distribution = .equalSpacing
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // add 3 labels to the stack view
        for _ in 1...3 {
            let v = UILabel()
            v.numberOfLines = 0
            // to make it easy to see the frames
            v.backgroundColor = .yellow
            stack.addArrangedSubview(v)
        }

        // red dashed-outline view to show the stack view frame
        let stackOutlineView = DashedOutlineView()
        
        [stackOutlineView, stack].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(v)
        }
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain stack view 20-pts from all 4 sides
            stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),

            // constrain outline view 1-pt around the stack view
            stackOutlineView.topAnchor.constraint(equalTo: stack.topAnchor, constant: -1.0),
            stackOutlineView.leadingAnchor.constraint(equalTo: stack.leadingAnchor, constant: -1.0),
            stackOutlineView.trailingAnchor.constraint(equalTo: stack.trailingAnchor, constant: 1.0),
            stackOutlineView.bottomAnchor.constraint(equalTo: stack.bottomAnchor, constant: 1.0),

        ])
        
        fillLabels()
        
        // tap anywhere to re-fill the labels
        let t = UITapGestureRecognizer(target: self, action: #selector(self.fillLabels))
        view.addGestureRecognizer(t)
    }
    
    @objc func fillLabels() -> Void {
        // set text of labels - from 1 to 8 lines
        let a: [Int] = Array(1...8).shuffled()
        for (v, n) in zip(stack.arrangedSubviews, a) {
            guard let lbl = v as? UILabel else { fatalError("This Demo should have only labels in the stack view!!") }
            lbl.text = ((1...n).map { "Line \([=10=])" }).joined(separator: "\n")
        }
    }
}

class DashedOutlineView: UIView {
    
    var shapeLayer: CAShapeLayer!
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        shapeLayer = self.layer as? CAShapeLayer
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.lineDashPattern = [8,8]
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.path = UIBezierPath(rect: bounds).cgPath
    }
}