看来,呈现和解除视图控制器都会提示呈现视图来布局其子视图和/或更新其约束。在视图层次较深的情况下,这会引入性能问题。再次 - 这是现有的,当前显示的视图。创建和显示的模态非常轻。
无论我是否使用autolayout(如我的示例项目中),都会发生这种情况。
我构建了一个demo project,它近似于我正在处理的应用。有一个主要的父控制器,水平滚动UIScrollView
。多个子控制器被添加到父控制器,并且它们的视图被添加到滚动视图并使用NSLayoutConstraint
进行排列。每个子视图本身都有一个子视图,一个简单的UIView
,也有一个约束。
在导航栏中,有一个用于启动模态的按钮。呈现时,父控制器多次在每个子视图上调用setNeedsLayout
。在我的演示项目中,我正在覆盖setNeedsLayout
以便在访问时进行记录。关闭模态时也会出现同样的情况。打开并关闭模态几次并观察控制台。
我看不出需要新布局的原因,而且对于更复杂的视图,我发现有数百个这样的调用正在触发,并且会对性能产生明显的影响。
请注意,当省略ChildView
的布局代码时,不会调用setNeedsLayout
。我鼓励您注释掉约束并查看日志记录的差异。
为什么会这样?如何在呈现和解除模态时阻止不必要的布局传递?
答案 0 :(得分:3)
首先,您正在记录setNeedsLayout
,这只是一种标记机制,并不会真正招致任何工作。多次调用setNeedsLayout
可能只会触发单个布局。您应该记录-[UIView layoutSubviews]
或-[UIViewController viewDidLayoutSubviews]
,因为这些都是实际繁重发生的地方。
其次,布局相关的方法是在演示期间重复和快速调用的,因为:
如果您想最大限度地减少布局次数,可以尝试使用presentViewController:animated:
放弃,而是使用addChildViewController:
并手动设置必要的视图。但即便如此,您仍然可以触发父控制器的布局。
答案 1 :(得分:1)
您正在做一件非常非常奇怪的事情:您正在维护一个带有10个子视图控制器的自定义父视图控制器,所有子视图控制器的所有视图都在接口中同时。视图控制器不是为那种东西而设计的。这就是触发您看到的多个layoutSubviews
来电。拥有多个子视图控制器很好,但是它们的视图不应该都在层次结构中 - 特别是在你的情况下,只有一个这样的子视图实际上是可见的。
实际上,您构建的界面 - 分页滚动视图,其中每个页面都是"是由视图控制器管理的视图 - 已经由UIPageViewController为您实现,它远更高效,因为它实际上一次只能维护最多三个视图控制器:视图控制器管理可见滚动视图中的视图,以及管理其右侧和左侧视图的视图控制器。它也非常方便,易于使用。
所以:
您应使用 UIPageViewController或
你应该模仿 UIPageViewController做什么,删除视图控制器'视图(甚至可能发布视图控制器)当它们滚动到视线外时 - 正如我们在UIPageViewController存在之前所做的那样 - 请参阅WWDC 2011的高级滚动视图技术视频。我的拉丁文"闪卡&#34 ;应用程序在UIPageViewController出现之前以这种方式工作;它有数千的词汇卡,每个词汇卡由一个视图控制器管理,但在任何一个时刻最多只能存在三个卡视图控制器。
(顺便说一句,您也不应该使用自己的self.childControllers
可变数组,因为此列表已经为您self.childViewControllers
维护。)
答案 2 :(得分:0)
我认为layoutSubviews
正在被调用,因为呈现控制器view
会更改超视图,同时在显示视图隐藏后动画出屏幕。
如果你想在框架没有改变的情况下跳过layoutSubviews
,只需保存对最后一帧的引用,如果等于返回而不做任何操作。此外,无需在子视图上调用setNeedslayout
,因为系统会在您调整大小时自动触发它。
无论如何,你的主要问题是你的方法:
如果您要使用视图控制器(仅在标签栏控制器内,推送到导航控制器,作为窗口' { {1}},以模态方式呈现,等等。如果要手动添加视图,请不要使用视图控制器,只使用自定义视图!这是一个非常常见的错误,您可以看到更多详细信息here。
懒惰加载视图和对象并重复使用。例如,您应该只加载1~3页内容并仅在用户滚动到它们时加载新内容。加载新视图时,请删除其中一个旧视图,或者更好地重复使用它。
您不仅可以将逻辑与控制器分开,还可以将自定义视图与逻辑分开。在特定情况下不应使用控制器的一些原因:
rootController
等事件。再次,因为你没有将它们用作正确的视图控制器。如果您正确实施了custom container controller(这是很多工作要做得很好),那么您可以使用控制器。否则坚持自定义视图。