【问题标题】:where to put manual layout code for a UIView where parent views use autolayout?在哪里放置父视图使用自动布局的 UIView 的手动布局代码?
【发布时间】:2017-02-23 02:17:34
【问题描述】:

当父视图本身使用自动布局时,我拥有的特定子 UIView 的手动布局代码(或触发它的位置)应该放在哪里?

注意事项:

  1. 视图的布局代码需要在任何父视图布局、方向更改、特征更改等之后使用其框架尺寸
  2. 我曾尝试在父视图控制器上使用 viewWillLayoutSubviews,但在多次调用它时遇到了困难 - 无法找到原因,但不想多次手动布局计算,因为它们很繁重.
  3. 因此,总体而言,对于以下各种场景,最终只有一次手动布局“触发”,并且在所有旋转和父视图布局都已完成时:

所以:

  • 加载时 (viewDidLoad)
  • 重新显示(类似视图随后出现)
  • 旋转
  • 性状变化

例如,我应该寻找一种方法:

a) 父视图控制器来控制它并确定何时向我的特定子视图(由它自己的视图控制器前置)发送“layoutSubviewNow”调用,但在这种情况下如何执行此操作,或者 b)

b) 让子视图对“layoutSubview()”做出反应,然后有一个委托可用于回调主控制器以请求布局数据?或者

c) 与 b) 类似,但使用子视图视图控制器“viewWillLayoutSubviews()”触发,然后有一个委托可用于回调主控制器以请求布局数据?

还是其他?

其实我在想这个主意:

d) 在主父视图控制器中:有一个实例变量 viewTransistionedOutstandingAction,并使用它以便 (i) 在 vi​​ewWillTransition 中将其设置为 true,然后在 (ii) viewWillLayoutSubviews 中执行以下操作:

if viewTransistionedOutstandingAction {
  self.triggerModelChange()
  viewTransistionedOutstandingAction = false
}

【问题讨论】:

  • 无论你想实现什么,请用截图或代码块说明。您在上面解释的内容只有您知道自己想要什么结果才能被您理解。简单地说,如果有人选择任何概念性答案,那么它可能对您的问题是错误的。
  • 好的。也许是一些伪代码
  • 您能简单地告诉我,您希望在控制器中的布局约束发生任何更改后调用一次触发函数吗?这是你想要的吗?
  • 差不多,但是在发生方向变化和父布局的时候。然后触发器调用子控制器告诉它进行布局计算。

标签: uiview uiviewcontroller autolayout layoutsubviews viewdidlayoutsubviews


【解决方案1】:

我有点不清楚您的具体要求是什么,但我可以为您的第一个问题提供答案:

当父视图本身使用自动布局时,我拥有的特定子 UIView 的手动布局代码(或触发它的位置)放在哪里?

UIView 实现中只有一个地方可以放置任何布局代码,那就是方法layoutSubviews()

布局引擎(根据你的约束计算实际帧的东西)在它为这个特定的视图计算出正确的帧之后调用这个方法。换句话说:layoutSubviews() 是布局过程中唯一一个可以确保视图已设置正确框架的位置。

你说得对,这个方法可能不止被调用一次,而是被调用多次。这是因为在某些情况下,需要 多次布局传递 来计算最终布局。 (示例:您有一些视图,其高度取决于其父视图的宽度,但父视图的高度取决于子视图的高度。)layoutSubviews() 方法也可以从代码中手动调用,例如在视图上调用 setNeedsLayout() 后跟 layoutIfNeeded() 也会导致在该视图上调用 layoutSubviews()

但是,系统只在需要的时候调用layoutSubviews()(除非您一次又一次地手动触发它),即当当前布局无效时。这可能有很多原因:设备旋转、约束更改、视图层次结构的更改等。但是这些是您的视图框架可能更改的情况,如果您的自定义布局取决于那个框架,它实际上应该也被重新计算。

因此,layoutSubviews() 是执行手动布局的正确位置,您应该避免在其他任何地方进行此操作。


注意:如果您正在处理 视图控制器 而不是普通视图,则执行布局更新的相应方法是 viewWillLayoutSubviews() (或viewDidLayoutSubviews(),如果您正在使用自动布局,并且只想在布局引擎解决所有子视图的约束后进行轻微修改)。

【讨论】:

  • 感谢 Mischa - 只是想知道人们如何解决布局计算的开销(例如,我实际上有一个 DIY 自定义滚动日历)不止一次重新调用 layoutSubviews?
  • 我不完全确定您所说的“开销”是什么意思。只有两种情况: 1. 你的布局依赖于视图的框架。然后你需要对每一帧的变化做出反应!是的,layoutSubviews() 再次被调用,但没有开销。 2. 你的布局独立于视图的框架(几乎从不如此)。然后你可以只做一次,例如在视图初始化期间。
  • 补充: 有可能会调用layoutSubviews(),即使视图的框架没有改变。这是您有开销的唯一情况。您可以通过将视图的当前帧存储在自定义属性 previousFrame 中来解决此问题,并且仅在 frame != previousFrame 时执行您的自定义布局代码。
  • 谢谢 - 这是有道理的,听起来是个好方法。所以你会在孩子的 viewDidLayoutSubviews() 中实现这个,假设此时父/封闭框架已经计算正确?
  • 是的。 (如果你说的是视图控制器。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多