如何通过优先级改变内在内容的大小

How to change the intrinsic content size through its priority

我希望第二个标签 (subview2) 保持原样并让第一个标签 (subview1) 随着容器视图尺寸的减小而缩小,但第二个标签仍然不管我怎么调整优先级都缩水:

import UIKit
import PlaygroundSupport

let containerView = UIView(frame: CGRect(origin: .zero, size: .init(width: 200, height: 300)))
let subview1 = UILabel()
subview1.text = "Hello"
subview1.backgroundColor = .red
containerView.addSubview(subview1)
subview1.translatesAutoresizingMaskIntoConstraints = false
let subview2 = UILabel()
subview2.text = "Hello"
subview2.backgroundColor = .cyan
containerView.addSubview(subview2)
subview2.translatesAutoresizingMaskIntoConstraints = false

let views: [String: Any] = ["subview1": subview1, "subview2": subview2]
let con1 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview1]", metrics: nil, views: views)
let con2 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview2]", metrics: nil, views: views)
let con3 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(30)-[subview1]", metrics: nil, views: views)
let con4 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview2]-(30)-|", metrics: nil, views: views)
let con5 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview1(>=100)]-(>=30)-[subview2(>=100)]", metrics: nil, views: views)
NSLayoutConstraint.activate(con1 + con2 + con3 + con4 + con5)

subview1.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: .horizontal)
subview2.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = containerView

我试过手动增加 subview2 的优先级,但还是没用:

let priority1 = subview2.contentCompressionResistancePriority(for: .horizontal)
print(priority1) // 750
let priority2 = subview2.contentCompressionResistancePriority(for: .horizontal)
print(priority2) // 750
subview2.setContentCompressionResistancePriority(priority2 + 100, for: .horizontal)

VFL(视觉格式语言)非常过时且受限。我强烈建议您改用更现代(也更灵活)的语法。

也就是说,问题出在这一行:

let con5 = NSLayoutConstraint.constraints(withVisualFormat: 
            "H:[subview1(>=100)]-(>=30)-[subview2(>=100)]", metrics: nil, views: views)

您的代码表示:

  • subview1宽度必须大于等于100
  • subview2宽度必须大于等于100

所以,setContentCompressionResistancePriority 行什么都不做。您已经说过“这些是 要求的 最小宽度。”

如果将该行更改为:

let con5 = NSLayoutConstraint.constraints(withVisualFormat:
            "H:[subview1]-(>=30)-[subview2]", metrics: nil, views: views)

您应该会得到想要的结果。

不过,您的代码使用了更现代的约束语法——您可能会发现它更合乎逻辑且更容易理解:

NSLayoutConstraint.activate([

    // both labels 20-pts from containerView Top
    subview1.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
    subview2.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),

    // left label Leading 30-pts from containerView Leading
    subview1.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 30.0),

    // right label Trailing 30-pts from containerView Trailing
    subview2.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -30.0),

    // right label Leading at least 30-pts from left label Trailing
    subview2.leadingAnchor.constraint(greaterThanOrEqualTo: subview1.trailingAnchor, constant: 30.0),

])

// don't do any of this...
//let views: [String: Any] = ["subview1": subview1, "subview2": subview2]
//let con1 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview1]", metrics: nil, views: views)
//let con2 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview2]", metrics: nil, views: views)
//let con3 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(30)-[subview1]", metrics: nil, views: views)
//let con4 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview2]-(30)-|", metrics: nil, views: views)
//let con5 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview1]-(>=30)-[subview2]", metrics: nil, views: views)
//NSLayoutConstraint.activate(con1 + con2 + con3 + con4 + con5)

编辑 - 回复评论...

如果您希望每个标签的宽度都为 >= 100,但允许它们压缩(首先压缩左标签),则需要添加优先级小于 [=20 的宽度约束=]:

// both labels should have a width of >= 100
let leftWidth = subview1.widthAnchor.constraint(greaterThanOrEqualToConstant: 100.0)
let rightWidth = subview2.widthAnchor.constraint(greaterThanOrEqualToConstant: 100.0)

// set the Priority on the width constraints to less than required
//  to allow auto-layout to break that constraint if needed
leftWidth.priority = .defaultHigh
rightWidth.priority = .defaultHigh

// activate the width constraints
leftWidth.isActive = true
rightWidth.isActive = true

// tell leftLabel to compress before rightLabel
subview1.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: .horizontal)
subview2.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)