何时调用layoutSubviews?


264

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

我有一个充满屏幕的视图。它在屏幕底部有一个自定义子视图,如果我更改导航栏的高度,该子视图将在Interface Builder中正确调整大小。layoutSubviews在创建视图时调用,但不再调用。我的子视图已正确布置。如果我将通话中的状态栏关闭,则layoutSubviews即使主视图会为其调整大小设置动画,也根本不会调用该子视图。

在什么情况下layoutSubviews实际上被称为?

我已autoresizesSubviews设置NO为我的自定义视图。在Interface Builder中,我具有顶部和底部的支撑杆以及垂直箭头设置。


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

[window makeKeyAndVisible];

其他子视图不会自动调整大小。

Answers:


492

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

  • init不致layoutSubviews被称为(duh)
  • addSubview:导致 layoutSubviews在要添加的视图,要添加到的视图(目标视图)以及目标的所有子视图上调用
  • 视图仅在框架的大小参数不同时才setFrame 智能调用layoutSubviews其框架已设置的视图
  • 滚动UIScrollView导致layoutSubviews在scrollView及其超级视图上被调用
  • 旋转设备仅layoutSubview在父视图(响应的viewControllers主视图)上调用
  • 调整视图大小将调用layoutSubviews其超级视图

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


1
好答案。我一直想知道layoutSubviews。是否initWithFrame:layoutSubviews被称为?
罗伯特

2
@Robert-我正在使用initWithFrame ...所以没有。
BadPirate 2011年

8
@BadPirate:是的。根据我的实验,如果你调整view1.1它调用layoutSubviewsview1,然后layoutSubviewsview1.1。该调用不会无限期地传播到父视图,view1.1.1layoutSubviewsview1.1和上的调用中调用它view1.1.1。仅移动而不更改其大小不会调用layoutSubviews任何一个。
若昂里斯本

1
没有在UIView上调用viewDidLoad(但在UIViewController上)。初始化UIView后,将调用View Did加载。
BadPirate

2
根据我的实验中,第二条规则可能是不准确的:当我添加view1.2view1layoutSubviewsview1.2view1被调用,但layoutSubviewsview1.1不叫。(view1.1view1.2是的子视图view1)。也就是说,并非目标视图的所有子视图都称为layoutSubviewsmethod
HongchaoZhang

96

在@BadPirate先前的回答的基础上,我进行了进一步的实验,并提出了一些说明/更正。我发现layoutSubviews:只有在以下情况下才会在视图上调用:

  • 它自己的边界(不是框架)已更改。
  • 其直接子视图之一的范围已更改。
  • 子视图将添加到视图或从视图中删除。

一些相关的细节:

  • 仅当新值不同(包括不同的原点)时,才认为边界发生了变化。特别要注意的layoutSubviews:是,每当UIScrollView滚动时都会调用它的原因,因为它通过更改其边界的原点执行滚动。
  • 如果更改了大小,则更改框架只会更改边界,因为这是唯一传播到bounds属性的东西。
  • 尚未在视图层次结构中的视图范围更改将导致调用layoutSubviews: 最终将视图添加到视图层次结构的时间
  • 只是为了完整性:这些触发器不会直接调用layoutSubviews,而是调用setNeedsLayout,它会设置/引发一个标志。对于视图层次结构中的所有视图运行循环的每次迭代都会检查此标志。对于发现标记升起的每个视图,将对其 layoutSubviews:调用并重置标记。将首先检查/调用层次结构中较高的视图。

5
无法对此答案表示足够的评价。它应该是最佳答案。给出的三个规则就是您所需要的。我从未遇到过这些规则无法完美描述的layoutSubview行为。
派克(Pärserk)

1
我不认为更改直接子视图的边界时会调用layoutSubviews。我认为layoutSubviews的调用只是测试的副作用。在某些情况下,子视图范围更改时,不会调用layoutSubviews。请检查frogcjn的答案,因为他的答案基于Apple的文档,而不仅仅是实验。
西蒙·贝克

19

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

每当视图中发生以下任何事件时,都可能发生布局更改:

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


另外,需要指出的重要一点是,当视图尚未添加到视图堆栈时,不会调用这些事件。您可以在其中加上“可以”一词,但是当这些事件之一将其标记为需要布局时,将在应用程序主线程的下一个可用循环中调用它。
user1122069'2

13

BadPirate的回答中的某些观点只是部分正确:

  1. 对于addSubView

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

    这取决于视图(目标视图)的自动调整大小蒙版。如果将autoresize mask设置为ON,则将在each上调用layoutSubview addSubview。如果没有自动调整大小的蒙版,则仅当视图(目标视图)的帧大小更改时,才会调用layoutSubview。

    示例:如果您以编程方式创建了UIView(默认情况下它没有自动调整大小的蒙版),则仅当UIView框架未在every上更改时,才会调用LayoutSubview addSubview

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

  2. 对于设备旋转点

    旋转设备仅在父视图(响应的viewController的主视图)上调用layoutSubview。

    仅当您的VC位于VC层次结构中(根位于window.rootViewController)时,这才是正确的,这是最常见的情况。在iOS 5中,如果您创建一个VC,但未将其添加到任何其他VC中,则当设备旋转时,该VC不会受到任何注意。因此,通过调用layoutSubviews不会注意到其视图。


9

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

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

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


您是说在调整视图大小时调用layoutSubviews吗?我一直
以为

它是。调整视图大小时,需要对其子视图执行某些操作。如果你不提供它,然后它移动它们会自动使用弹簧,支撑件等
史蒂夫·韦勒

8

[self.view setNeedsLayout]; 在viewController中调用 使其可以调用viewDidLayoutSubviews


5

您是否看过layoutIfNeeded?

文档片段如下。如果在动画过程中显式调用此方法,动画是否起作用?

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

- (void)layoutIfNeeded

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

可用性在iPhone OS 2.0和更高版本中可用。


2

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

6个小时的无奈结束了。


您是否按下了窗口键?如果没有,这可能导致各种有趣的事情都不会发生。
Steve Weller 2010年

-2

layoutSubviews永远不会被调用的一个相当晦涩但潜在的重要案例是:

import UIKit

class View: UIView {

    override class var layerClass: AnyClass { return Layer.self }

    class Layer: CALayer {
        override func layoutSublayers() {
            // if we don't call super.layoutSublayers()...
            print(type(of: self), #function)
        }
    }

    override func layoutSubviews() {
        // ... this method never gets called by the OS!
        print(type(of: self), #function)
    }
}

let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

1
为什么这个答案在这里?
多米尼克·布彻
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.