为啥 UITableView 滚动时会更新约束?

Posted

技术标签:

【中文标题】为啥 UITableView 滚动时会更新约束?【英文标题】:Why constraints are updated on scrolling of UITableView?为什么 UITableView 滚动时会更新约束? 【发布时间】:2018-12-28 14:54:40 【问题描述】:

我有 2 个简单的 UILabel 需要水平放置。其中一个在视图的右侧,而第二个在左侧并获得所有可能的空间。这是我的限制:

    amountOfQuestions.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
    amountOfQuestions.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true


    topicName.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
    topicName.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
    topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8).isActive = true
    topicName.rightAnchor.constraint(equalTo: amountOfQuestions.leftAnchor, constant: -8).isActive = true

看起来一切正常,但是当显示表格视图时,它看起来像这样:

但是在我上下滚动我的表格几次后,它就变得正常了:

为什么我的表格没有像第二张图片那样立即显示?

解决办法:

amountOfQuestions.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
    amountOfQuestions.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    amountOfQuestions.setContentHuggingPriority(.required, for: .horizontal)
    amountOfQuestions.setContentCompressionResistancePriority(.required, for: .horizontal)

    topicName.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
    topicName.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
    topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8).isActive = true
    topicName.rightAnchor.constraint(equalTo: amountOfQuestions.leftAnchor, constant: -8).isActive = true
    topicName.setContentHuggingPriority(.required, for: .horizontal)
    topicName.setContentCompressionResistancePriority(.required, for: .vertical)


    let myBottom = topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
    myBottom.priority = UILayoutPriority(rawValue: UILayoutPriority.required.rawValue - 1)
    myBottom.isActive = true

【问题讨论】:

你在哪里设置约束?另请注意,底部约束应将优先级设置为999(不是必需但高优先级),并且您需要将左侧标签上的垂直拥抱优先级和抗压优先级设置为1000(必需)。并且您必须为正确的标签设置水平拥抱优先级和抗压性。如果你不这样做,那么标签内容将不会被正确考虑。 谢谢。我有问题地展示了我是如何设置约束的。但是,为什么需要你告诉的那些限制?他们如何解决问题?为什么不使用它们就无法实现目标? 【参考方案1】:

有多个问题会影响布局并使其定义不正确:

    永远不要将内容限制在单元格本身。始终限制为单元格的contentView,例如:

    amountOfQuestions.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -8).isActive = true
    

(确保将标签添加为 contentView 的子视图,而不是单元格本身的子视图)。

    如果您希望标签的大小产生任何影响,则需要正确设置拥抱和抗压优先级。否则布局不必尊重内容:

    // you want topic name to resize vertically to respect the content
    topicName.setHuggingPriority(.required, for: .vertical)
    topicName.setContentCompressionResistancePriority(.required, for: .vertical)
    
    // you want amount of question to resize horizontally to respect the content
    amountOfQuestions.setHuggingPriority(.required, for: .horizontal)
    amountOfQuestions.setContentCompressionResistancePriority(.required, for: .horizontal)
    

    为避免在第一次布局期间出现布局冲突,最好将其中一个垂直约束设置为稍低的布局优先级:

    let bottomAnchor = topicName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
    bottomAnchor.priority = UILayoutPriority(rawValue: UILayoutPriority.required.rawValue - 1)
    bottomAnchor.isActive = true
    

【讨论】:

您对 contentView 的第一个建议失败了。没有 contentView 工作,有了它,它就不起作用。 @abay 描述“不起作用”。您是否使用contentView.addSubview(topicName) 将标签添加到contentView 我有罪。对不起。但是你能澄清一下为什么需要使用 contentView 吗?它有什么好处? @abay 好吧,内容视图不必与单元格本身具有相同的大小。将配件添加到单元格时,您可以看到差异。然后contentView 变小,如果您将内容限制在单元格本身,它将与附件冲突。在编辑模式(例如单元格删除按钮)或滑动操作中会出现类似问题。【参考方案2】:

1- UILabel 有一个内在的内容值,这意味着你不必为它们指定宽度/高度,所以当你像这样水平放置它们时

| - lbl1 - lbl2 - |

这会导致约束模糊,因为当一个内容太大时,自动布局需要知道要修剪哪个标签,这就是为什么您需要将水平压缩阻力 && setHuggingPriority 设置为 lbl2 = 1000

2- 您需要降低底部约束的优先级,因为单元格在布局开始时假定固定高度,并且该高度可能与垂直内容的当前总高度冲突,因此您降低它以避免这种冲突

【讨论】:

以上是关于为啥 UITableView 滚动时会更新约束?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 UITableView 单元格在滚动时重叠?

UITableView 仅在向上滚动时更新,而不是向下滚动

当 SuperViews 约束改变时更新 SubView 约束

为啥滚动 UITableView 比滚动 UIScrollView 响应更快?

UITableCell 中的 UILabel 大小不正确,直到 UITableView 滚动

为啥即使在我将约束设置为 0 后 UITableView 上仍有余量