【问题标题】:When is layoutSubviews called?什么时候调用 layoutSubviews?
【发布时间】:2010-10-18 05:18:25
【问题描述】:

我有一个自定义视图在动画期间没有收到layoutSubview 消息。

我有一个充满屏幕的视图。如果我更改导航栏的高度,它在屏幕底部有一个自定义子视图,可以在 Interface Builder 中正确调整大小。 layoutSubviews 在创建视图时被调用,但不再调用。我的子视图布局正确。如果我关闭通话状态栏,则根本不会调用子视图的 layoutSubviews,即使主视图确实对其调整大小进行了动画处理。

layoutSubviews实际是在什么情况下调用的?

我将 autoresizesSubviews 设置为 NO 用于我的自定义视图。在 Interface Builder 中,我设置了顶部和底部支柱以及垂直箭头。


难题的另一部分是必须将窗口设为关键:

[window makeKeyAndVisible];

否则子视图不会自动调整大小。

【问题讨论】:

    标签: ios cocoa-touch autolayout bounds layoutsubviews


    【解决方案1】:

    我有一个类似的问题,但对答案(或我能在网上找到的任何答案)不满意,所以我在实践中尝试了一下,结果如下:

    • init 不会导致 layoutSubviews 被称为(duh)
    • addSubview: 原因 layoutSubviews 被调用 正在添加的视图,正在添加的视图 添加到(目标视图),以及所有 目标的子视图
    • 查看setFrame 智能呼叫layoutSubviews on 仅设置了框架的视图 如果框架的尺寸参数是 不一样的
    • 滚动 UIScrollView 导致layoutSubviews 被调用 scrollView 及其父视图
    • 旋转设备仅调用 layoutSubview 在父视图上( 响应 viewControllers 主要 查看)
    • 调整视图大小将在其父视图上调用layoutSubviews重要提示: 如果决定其大小的内容发生变化,则具有固有内容大小的视图将重新调整大小;例如,更新UILabel 上的文本将导致更新内在内容大小,因此在其父视图上调用 layoutSubviews)

    我的结果 - http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/

    【讨论】:

    • 很好的答案。我一直想知道layoutSubviewsinitWithFrame: 是否会导致 layoutSubviews 被调用?
    • @Robert - 我使用的是 initWithFrame... 所以没有。
    • @BadPirate:是的。根据我的实验,如果你调整view1.1 的大小,它会调用layoutSubviewsview1,然后调用layoutSubviewsview1.1。这个调用不会无限期地传播到超级视图,在view1.1.1 上调用它只会在view1.1view1.1.1 上调用layoutSubviews。只是移动而不改变它的大小不会在其中任何一个上调用layoutSubviews
    • viewDidLoad 不会在 UIView 上调用(而是 UIViewController)。在 UIView 初始化后调用 View 加载。
    • 根据我的实验,第二条规则可能不准确:当我将view1.2添加到view1中时,view1.2view1layoutSubviews被调用,但是layoutSubviewsview1.1 未被调用。 (view1.1view1.2view1 的子视图)。 也就是说,并不是目标视图的所有子视图都调用layoutSubviews方法
    【解决方案2】:

    在@BadPirate 之前的回答的基础上,我做了一些进一步的实验,并提出了一些澄清/更正。我发现layoutSubviews: 将在视图上被调用当且仅当:

    • 它自己的bounds(不是框架)发生了变化。
    • 其直接子视图之一的边界已更改。
    • 子视图被添加到视图中或从视图中删除。

    一些相关细节:

    • 仅当新值不同时,边界才被视为已更改,包括不同的原点。请特别注意,这就是为什么每当 UIScrollView 滚动时都会调用 layoutSubviews:,因为它通过更改其边界的原点来执行滚动。
    • 更改框架只会在大小发生更改时更改边界,因为这是唯一传播到边界属性的内容。
    • 当视图最终添加到视图层次结构中时,尚未在视图层次结构中的视图边界更改将导致调用layoutSubviews:
    • 只是为了完整性:这些触发器不直接调用 layoutSubviews,而是调用 setNeedsLayout,它设置/引发一个标志。运行循环的每次迭代,对于视图层次结构中的所有视图,都会检查此标志。对于发现标志的每个视图,在其上调用layoutSubviews: 并重置标志。层次较高的视图将首先被检查/调用。

    【讨论】:

    • 不能足够支持这个答案。它应该是最佳答案。给出的三个规则就是你所需要的。我从来没有遇到过这些规则没有完美描述的任何 layoutSubview 行为。
    • 我认为 layoutSubviews 在直接子视图的边界发生更改时不会被调用。我认为 layoutSubviews 的调用只是您测试的副作用。在某些情况下,当子视图边界发生变化时,不会调用 layoutSubviews。请检查 frogcjn 的答案,因为他的 anwser 是基于 Apple 的文档,而不仅仅是实验。
    【解决方案3】:

    https://developer.apple.com/library/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_ref/doc/uid/TP40009503-CH5-SW1

    只要发生以下任何事件,都可能发生布局更改 在视图中:

    一个。视图边界矩形的大小发生变化。
    湾。发生界面方向更改,这通常会触发根视图的边界矩形的更改。
    C。与视图层关联的一组核心动画子层发生变化并需要布局。
    d。您的应用程序通过调用视图的setNeedsLayoutlayoutIfNeeded 方法来强制进行布局。
    e.您的应用程序通过调用视图底层对象的setNeedsLayout 方法来强制布局。

    【讨论】:

    • 另外,需要指出的是,当视图尚未添加到视图堆栈时,不会调用这些事件。您将其与“can”一词一起包含在内,但具体而言,当其中一个事件将其标记为需要布局时,它会在应用程序主线程的下一个可用周期中调用。
    【解决方案4】:

    打电话 [self.view setNeedsLayout]; 在 viewController 中调用 viewDidLayoutSubviews

    【讨论】:

      【解决方案5】:

      BadPirate's answer 中的一些观点只是部分正确:

      1. 对于addSubView

        addSubview 导致 layoutSubviews 在被添加的视图、被添加到的视图(目标视图)以及目标的所有子视图上调用。

        这取决于视图的(目标视图)自动调整大小掩码。如果它启用了自动调整掩码,layoutSubview 将在每个addSubview 上调用。如果它没有自动调整大小掩码,那么 layoutSubview 将仅在视图的(目标视图)帧大小发生变化时被调用。

        示例:如果您以编程方式创建 UIView(默认情况下它没有自动调整大小掩码),则仅当 UIView 框架更改而不是每个 addSubview 时才会调用 LayoutSubview。

        正是通过这种技术,应用程序的性能也得到了提高。

      2. 对于设备旋转点

        旋转设备只调用父视图(响应视图控制器的主视图)上的 layoutSubview

        只有当您的 VC 位于 VC 层次结构中时(根位于 window.rootViewController),这才是正确的,这是最常见的情况。在 iOS 5 中,如果你创建了一个 VC,但它没有添加到任何另一个 VC 中,那么当设备旋转时,这个 VC 就不会被注意到。因此,调用 layoutSubviews 不会注意到它的视图。

      【讨论】:

        【解决方案6】:

        将 OpenGL 应用程序从 SDK 3 迁移到 4 时,不再调用 layoutSubviews。经过大量试验和错误后,我终于打开了 MainWindow.xib,选择了 Window 对象,在检查器中选择了 Window Attributes 选项卡(最左侧)并选中了“Visible at launch”。似乎在 SDK 3 中它仍然会导致 layoutSubViews 调用,但在 4 中没有。

        6 小时的挫败感告一段落。

        【讨论】:

        • 你做了窗口键吗?如果没有,这可能会导致所有有趣的事情都不会发生。
        【解决方案7】:

        我将解决方案追溯到 Interface Builder 坚持认为在打开了模拟屏幕元素(状态栏等)的视图上不能更改弹簧。由于主视图的弹簧已关闭,因此该视图无法更改大小,因此在出现通话栏时会整体向下滚动。

        关闭模拟功能,然后调整视图大小并正确设置弹簧会导致动画发生并调用我的方法。

        调试中的一个额外问题是,当通过菜单切换通话状态时,模拟器会退出应用程序。退出应用 = 没有调试器。

        【讨论】:

        • 你是说layoutSubviews在视图调整大小时被调用?我一直以为不是……
        • 是的。当视图被调整大小时,它需要对其子视图做一些事情。如果你不提供它,那么它会使用弹簧、支柱等自动移动它们。
        【解决方案8】:

        你看过 layoutIfNeeded 吗?

        文档 sn-p 如下。如果在动画期间显式调用此方法,动画是否有效?

        layoutIfNeeded 如果需要,布置子视图。

        - (void)layoutIfNeeded
        

        讨论 使用此方法在绘制之前强制子视图的布局。

        可用性 适用于 iPhone OS 2.0 及更高版本。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-12-25
          • 2014-03-27
          • 1970-01-01
          • 2016-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多