调用 setNeedsLayout 或 setNeedsDisplay 的场合

Posted

技术标签:

【中文标题】调用 setNeedsLayout 或 setNeedsDisplay 的场合【英文标题】:The occasion to call setNeedsLayout or setNeedDisplay 【发布时间】:2018-05-18 07:48:13 【问题描述】:

我看过一些文章,说setNeedsLayout和layoutIfNeeded有什么区别,而我关注的是:

1.如果我想立即布局,是否需要将这两个方法一起调用,因为我多次看到这种组合

2。我什么时候需要调用 setNeedsLayout?据我了解,如果我更改视图的框架,它将在下一个周期更新布局,我不必显式调用 setNeedsLayout

【问题讨论】:

setNeedsLayout and setNeedsDisplay的可能重复 【参考方案1】:

这些东西的工作原理是通过失效来消除冗余。如果需要布局,视图将包含信息。

所以调用setNeedsLayout 只会将一些内部布尔值needsLayout 设置为true。一旦layoutIfNeeded 被调用,它将检查这个布尔值

if needsLayout  
    needsLayout = false
    doMagic() // Calls layoutSubviews at some point

之所以这样设计,是因为多次调用可能会使布局无效,但我们只想布局一次或尽可能少地布局。

在大多数情况下,您不需要致电setNeedsLayout,因为大多数更改已经为您完成了这项工作。例如,您可以更改约束值并为您完成失效。您只需致电layoutIfNeeded,您的观点就会更新。为了更正确,您甚至不需要致电layoutIfNeeded,因为它会在下一个周期为您执行此操作。但是,如果您希望更改动画并且需要在动画块中执行此操作,则需要调用它。

myViewConstraint.constant = 40.0 // Will already call setNeedsLayout
UIView.animate(withDuration: 0.3, animations: 
    myView.layoutIfNeeded()
)

所以改变约束只会改变视图应该如何布局的信息。只有对layoutIfNeeded 的调用才会真正使用这些值并更改布局。这就是为什么你只需要把它放在动画块中(虽然把它全部放在块中并没有错)。

公平地说,已经有一些变化,现在默认情况下(您可以禁用它)动画方法已经自行布局您的视图,因此您可以使用更少的代码进行操作,但这不是目前的重点。

所以:

    您不需要立即同时调用这 2 个方法来布局。如果视图布局已经失效(在大多数情况下),那么layoutIfNeeded 就足够了。但请注意,setNeedsLayout 就像在内部将布尔值设置为 true 一样微不足道,因此调用它没有害处,只是一种预防措施。所以调用两者更安全。不过,单独调用 setNeedsLayout 不会“立即”执行任何操作。

    希望您永远不需要致电setNeedsLayout。在一些复杂的情况下,您需要显式地使布局无效,并且存在一些可能的 UI 错误。在所有其他情况下,这将为您完成。但请注意,如果您遇到需要称其为“它将在下一个周期更新布局”的情况,则将不正确。在视图布局失效之前,它根本不会布局。

我不确定setNeedDisplay 适合您的问题(仅在您的标题中),但这个问题的工作方式相同,但有点复杂。它将使其内容无效并强制其重绘,调用drawRect。这必须在其绘制管道期间发生,而不仅仅是任何时候,因此您可能不会显式调用它来重绘。如果你什么都不做(可能会崩溃),因为它没有可以利用的上下文。如果您覆盖drawRect 并调整视图大小,它将尝试缓存您绘制的内容并使用contentMode 调整绘图大小。默认情况下,它设置为scaleToFill,这意味着您的内容将随着视图大小的变化而被拉伸。您需要致电setNeedDisplay 以便再次调用您的drawRect,并且您可以相应地重绘内容。

【讨论】:

嗨,@Matic,很好的答案。还有一个关于“在大多数情况下您不需要调用 setNeedsLayout,因为大多数更改已经为您完成了。例如,您可以更改约束值并为您完成无效”的另一个问题。你能分享在哪里可以找到它(也许在文档中)? @pacification 正如我上面所说的答案,仅供参考 @pacification 我从未见过专门描述这一点的文档。它可能在文档中的某个地方出现,但很可能只是在某个地方提到过。问题是这种逻辑可能会随着时间的推移在内部发生变化,并且已经在多个版本的操作系统上这样做了。关键是如果一切顺利,你永远不会调用失效,但如果在某个时候你看到异常,它可能是修复它的原因。请注意,整个 UI 布局和渲染是极其复杂的事情。主要是由于优化。所以内部很容易出错。【参考方案2】:

我找到了我想问的问题,什么时候应该调用 setNeedLayout,什么时候不应该。由于调用setNeedLayout的目的是调用layoutSubviews,所以对于以下情况,我不必调用setNeedLayout

调整视图大小 添加子视图用户 滚动 UIScrollView (在 UIScrollView 及其父视图上调用 layoutSubviews) 用户旋转他们的设备 更新视图的约束

【讨论】:

这是一个我认为很有帮助的博客tech.gc.com/demystifying-ios-layout。

以上是关于调用 setNeedsLayout 或 setNeedsDisplay 的场合的主要内容,如果未能解决你的问题,请参考以下文章

setNeedsDisplay setNeedsLayout

UIView 子视图中的 setNeedsLayout

iOS UIView常用的一些方法setNeedsDisplay和setNeedsLayout 区别

iOS开发:setNeedsLayOut和setNeedsDisplay区别

setNeedsLayout 和 setNeedsDisplay

setNeedsLayout 仅在显示后重新布局单元格