当框架小于所需时,为 UIStackView 中的第一个 UILabel 提供更高的优先级

Give more priority for the first UILabel in a UIStackView when frame is smaller than required

我有一个标签数组:[UILabel],计数是根据数组中元素的数量计算的。我通过循环将它们添加到 UIStackView 中。在某一点上,UILabels 的数量超过了 UIStackView 的帧,在这种情况下,我希望显示先添加的 UILabels,然后隐藏后添加的 UILabels。发生的事情恰恰相反。像这样:-

我希望 text 1 优先于 text 2 显示,依此类推。这是代码:

override func viewDidLoad() {
    super.viewDidLoad()

    let stackView = UIStackView()
    stackView.alignment = .top
    stackView.axis = .vertical

    view.addSubview(stackView)
    stackView.center = view.center
    stackView.frame.size = CGSize(width: 100, height: 24)

    (0..<4).forEach {
        let label = UILabel()
        label.text = "text \([=10=])"
        label.backgroundColor = .red
        stackView.addArrangedSubview(label)
    }
}

仅供参考:我不想颠倒订单。

更新:感谢@DonMag 和@Rob,我在我的代码中设置了内容抗压缩优先级。不幸的是,出于某种原因,我得到的结果与@DonMag 在他的屏幕截图中描述的结果不同。这是我更新的代码:

override func viewDidLoad() {
    super.viewDidLoad()

    let stackView = UIStackView()
    stackView.alignment = .leading
    stackView.axis = .vertical

    view.addSubview(stackView)
    stackView.frame = CGRect(x: 100, y: 100, width: 100, height: 150)

    (0..<15).forEach {
        let label = UILabel()
        label.text = "text \([=11=])"
        label.backgroundColor = .red
        stackView.addArrangedSubview(label)
    }
    var p: Float = 1000
    for v in stackView.arrangedSubviews {
        v.setContentCompressionResistancePriority(UILayoutPriority(rawValue: p), for: .vertical)
        p -= 1
    }
}

这就是我所说的结果:

应该这样做:

    var p: Float = 1000
    for v in stackView.arrangedSubviews {
        v.setContentCompressionResistancePriority(UILayoutPriority(rawValue: p), for: .vertical)
        p -= 1
    }
    

虽然使用堆栈视图的方式有点奇怪,但它可能无法满足您的需求。

例如,使用 100 x 150 帧和 14 个标签,我们得到:

如我们所见,底部标签有点“偏离”。

而且,只有 5 个标签,我们得到:

顶部标签的高度会被拉伸,除非我们对 Content Hugging 优先级做同样的事情...在这种情况下底部标签会被拉伸。

更好的方法是将堆栈视图嵌入 UIView,约束 Top / Leading / Trailing,设置“容器”的高度 UIView,然后确保设置.clipsToBounds = true 在容器视图上。

有 14 个标签:

有 5 个标签:

这是要玩的代码...

方法一:

class FrankMethod1ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stackView = UIStackView()
        stackView.alignment = .leading
        stackView.axis = .vertical
        
        view.addSubview(stackView)
        stackView.frame = CGRect(x: 100, y: 100, width: 100, height: 150)
        
        // change to (0..<5) to see the stretching
        (0..<15).forEach {
            let label = UILabel()
            label.text = "text \([=11=])"
            label.backgroundColor = .red
            stackView.addArrangedSubview(label)
        }

        var p: Float = 1000
        for v in stackView.arrangedSubviews {
            v.setContentCompressionResistancePriority(UILayoutPriority(rawValue: p), for: .vertical)
            p -= 1
        }

    }

}

方法二:

class FrankMethod2ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let cView = UIView()
        cView.frame = CGRect(x: 100, y: 100, width: 100, height: 150)
        cView.clipsToBounds = true
        
        // so we can see the view frame
        cView.backgroundColor = .cyan
        
        view.addSubview(cView)
        
        let stackView = UIStackView()
        stackView.alignment = .leading
        stackView.axis = .vertical
        
        cView.addSubview(stackView)
        
        stackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: cView.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: cView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: cView.trailingAnchor),
        ])
        
        // change to (0..<5) to see NO stretching
        (0..<15).forEach {
            let label = UILabel()
            label.text = "text \([=12=])"
            label.backgroundColor = .red
            stackView.addArrangedSubview(label)
        }
        
    }
    
}

编辑

我认为 Playground 执行与 Sim/Device 之间存在差异并不罕见。

这是我得到的:

当 运行 此代码在 Playground 中时...前两个是带有 15 和 5 个标签的“方法 1”,后两个是带有 15 和 5 个标签的“方法 2”:

import UIKit
import PlaygroundSupport

class PlaygroundFrankViewController: UIViewController {

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stackView1 = UIStackView()
        stackView1.alignment = .leading
        stackView1.axis = .vertical
        
        view.addSubview(stackView1)
        stackView1.frame = CGRect(x: 50, y: 50, width: 100, height: 150)
        
        // change to (0..<5) to see the stretching
        (0..<15).forEach {
            let label = UILabel()
            label.text = "text \([=13=])"
            label.backgroundColor = .red
            stackView1.addArrangedSubview(label)
        }
        
        var p: Float = 1000
        for v in stackView1.arrangedSubviews {
            v.setContentCompressionResistancePriority(UILayoutPriority(rawValue: p), for: .vertical)
            p -= 1
        }

        let stackView2 = UIStackView()
        stackView2.alignment = .leading
        stackView2.axis = .vertical
        
        view.addSubview(stackView2)
        stackView2.frame = CGRect(x: 200, y: 50, width: 100, height: 150)
        
        // change to (0..<5) to see the stretching
        (0..<5).forEach {
            let label = UILabel()
            label.text = "text \([=13=])"
            label.backgroundColor = .red
            stackView2.addArrangedSubview(label)
        }
        
        p = 1000
        for v in stackView2.arrangedSubviews {
            v.setContentCompressionResistancePriority(UILayoutPriority(rawValue: p), for: .vertical)
            p -= 1
        }
        
        
        let cView1 = UIView()
        cView1.frame = CGRect(x: 50, y: 220, width: 100, height: 150)
        cView1.clipsToBounds = true
        
        // so we can see the view frame
        cView1.backgroundColor = .cyan
        
        view.addSubview(cView1)
        
        let stackView3 = UIStackView()
        stackView3.alignment = .leading
        stackView3.axis = .vertical
        
        cView1.addSubview(stackView3)
        
        stackView3.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            stackView3.topAnchor.constraint(equalTo: cView1.topAnchor),
            stackView3.leadingAnchor.constraint(equalTo: cView1.leadingAnchor),
            stackView3.trailingAnchor.constraint(equalTo: cView1.trailingAnchor),
        ])
        
        // change to (0..<5) to see NO stretching
        (0..<15).forEach {
            let label = UILabel()
            label.text = "text \([=13=])"
            label.backgroundColor = .red
            stackView3.addArrangedSubview(label)
        }
        
        let cView2 = UIView()
        cView2.frame = CGRect(x: 200, y: 220, width: 100, height: 150)
        cView2.clipsToBounds = true
        
        // so we can see the view frame
        cView2.backgroundColor = .cyan
        
        view.addSubview(cView2)
        
        let stackView4 = UIStackView()
        stackView4.alignment = .leading
        stackView4.axis = .vertical
        
        cView2.addSubview(stackView4)
        
        stackView4.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            stackView4.topAnchor.constraint(equalTo: cView2.topAnchor),
            stackView4.leadingAnchor.constraint(equalTo: cView2.leadingAnchor),
            stackView4.trailingAnchor.constraint(equalTo: cView2.trailingAnchor),
        ])
        
        // change to (0..<5) to see NO stretching
        (0..<5).forEach {
            let label = UILabel()
            label.text = "text \([=13=])"
            label.backgroundColor = .red
            stackView4.addArrangedSubview(label)
        }

    }
    
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = PlaygroundFrankViewController()