为什么在调试视图层次结构时会出现这些布局问题,而模拟器中的 UI 布局还可以

Why these layout issues occur when debugging view hierarchy while the UI layout in simulator is OK

我使用视觉格式在单元格中布局子视图,如下所示:

contentView.addSubview(accountLabel)
contentView.addSubview(contentLabel)
contentView.addSubview(timeLabel)

let views: [String : Any] = [
    "accountLabel": accountLabel,
    "contentLabel": contentLabel,
    "timeLabel": timeLabel
]

let hConstraint1 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[accountLabel]-[timeLabel]-8-|", options: [.alignAllCenterY], metrics: nil, views: views)
let hConstraint2 = NSLayoutConstraint.constraints(withVisualFormat: "H:[contentLabel]-8-|", options: [], metrics: nil, views: views)
let vConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|-12-[accountLabel]-8-[contentLabel]-12-|", options: [.alignAllLeading], metrics: nil, views: views)
NSLayoutConstraint.activate(hConstraint1+hConstraint2+vConstraint)

在模拟器中 运行 时看起来还不错:

但是,调试视图层次结构时会出现一些布局问题:

为什么在 运行 时间 UI 看起来正常的情况下会出现这些布局问题?

上面的视觉格式字符串有问题吗?还是Xcode的bug?

如何解决这些布局问题?

好吧,我不是有限制的专家,但我确实看到了一个 "correctable" 问题和一个 "that's just weird maybe it's a bug" 问题。

据我了解,自动布局将尝试满足约束条件——即使它们不明确。如果它 失败 ,那么您会在控制台中看到错误消息,通常是 "will attempt to satisfy by breaking..."

因此您在 Debug View Hierarchy 中看到的警告信息比 "fix this error" 指令更具信息性。

也就是说...


您有 accountLabeltimeLabel 相对约束,但它们都具有相同的 Compression Resistance。因此,只要每个标签中的文本量 "fit",就可以了。

但是假设 accountLabel 得到的文本字符串是 "This user name is too long to fit here"?

它应该是这样的吗(我使用背景颜色来帮助查看框架):

或者像这样:

这就是水平位置和宽度显示为 "ambiguous" 的原因。

您可以通过添加一行来解决该问题。假设您希望 accountLabel 被截断:

    accountLabel.setContentCompressionResistancePriority(749, for: .horizontal)

会成功的。默认值为 750,因此该行告诉自动布局给予 timeLabel 更高的阻力。


显示为不明确的高度和垂直位置似乎不正确。我没有发现约束有任何问题。

如果我对所有三个标签使用相同的字体? 没有 不明确的警告。

如果我为 timeLabel 使用不同的字体大小(如您在图片中显示的那样),现在 调试视图层次结构中的警告。

除了... 如果我滚动以便重复使用单元格?警告已消失!

编辑: 正如 OP 在他的评论中所述,可以通过为 [=13= 提供不同的 Content Hugging Priority 值来消除垂直歧义(在滚动之前) ] 和 contentLabel.

因为默认的 Hugging 优先级是 250:

    accountLabel.setContentHuggingPriority(249, for: .vertical)

清除该警告。

我不认为它是必要的...但它确实似乎可以完成这项工作。