使用UIKit视图操作时,整体视图层次结构如何更改?

4
我一直在尝试弄清楚当使用pushViewController:animated, presentModalViewController:animated, UITabBarController中的选项卡切换以及UIAlertViewUIActionSheet方法时,视图层次结构中会发生什么。另外,我正在做这个是因为我需要知道当我不知道它或它的超级视图可能如何添加到视图层次结构中时,我创建的特定UIView是否可见于屏幕上。如果有人知道一个好的方法来确定这一点,我会欢迎这方面的知识。为了找出答案,我已经记录下[[UIApplication sharedApplication] keyWindow]子视图在不同情况下的层级关系。以下几点是否正确:
  1. 当一个新的viewController被推入UINavigationController的堆栈时,旧的viewController的视图不再在视图层次结构中。也就是说,只有位于堆栈顶部的view controller的视图是UINavigationController视图的子视图(根据日志,实际上是几个私有类,比如UILayoutContainerView)。那么,堆栈顶部以下的view controller的视图是否真的被从窗口中移除了呢?

  2. 当通过presentModalViewController:animated呈现一个新的viewController时,新viewController的视图是kew window的唯一子视图。这个说法正确吗?

  3. 最容易理解的情况:UIAlertView会创建它自己的窗口,并将其设为关键窗口。

  4. 我遇到的最奇怪的事情是:当通过showInView:方法显示UIActionSheet时,该actionSheet根本不在视图层次结构中。它不是传递给showInView:方法的视图的子视图,也没有添加为关键窗口的子视图,也没有创建它自己的窗口。那么它是如何出现的呢?

  5. 我还没有尝试过这个,所以我想知道当UITabBarController中的选项卡切换时,在keyWindow层次结构中会发生什么。被选中的UIViewController的视图是否移动到顶部,或者它是否像pushViewController:animatedpresentModalViewController:animated中那样,只有显示的视图在视图层次结构中?

3个回答

1

我认为你在概念上混淆了视图和视图控制器。导航控制器和选项卡控制器都管理其他视图控制器,但它们都不管理视图。

整个应用程序没有视图层次结构,只有视图控制器的层次结构。只有当每个受控制的视图被加载时,才会存在视图层次结构。然后,您将获得一个临时的窗口层次结构 -> viewController.view -> viewController.view.subviews。当您在堆栈上推入/弹出另一个视图控制器时,您将获得另一个视图层次结构。

视图层次结构是用户看到的舞台幻觉。视图控制器层次结构是程序员用来创建该幻觉的舞台布景/后台。不要混淆这两者。

所以:

  1. 堆栈中顶部控制器以下的视图控制器的视图实际上会从窗口中移除吗?是的。当视图控制器从堆栈中弹出时,其视图会消失。为什么要浪费内存来持有用户可能再也看不到的视图呢?
  2. 新的视图控制器的视图是关键窗口的唯一子视图。这是正确的吗?是的。
  3. ...一个UIAlertView创建了自己的窗口并使其成为关键窗口。是的。
  4. ...actionSheet根本不在视图层次结构中。那么它是如何出现的呢?它由应用程序添加到窗口之上。这就是它的特殊之处。这也是它能够在其他视图失败时出现在所有其他视图之上的原因。
  5. 所选UIViewController的视图是否移到了顶部是的。同样,这是舞台魔术。您将一个控制器的视图替换为另一个控制器的视图。

1
(1) 是的,一个视图被替换为另一个视图。任何时候只有一个viewController.view是活跃的。 (2) 警报和UISheet在技术上是窗口的同级视图,即它们存在于层次结构的相同顶级。因此,据我所知,你不会在窗口的子视图转储中看到它们(至少是警报)。 - TechZen
谢谢,但是我看到的日志显示UIAlertView是其自身窗口(类别为_UIAlertOverlayWindow)的子视图,当alertView在展示的时候,这个窗口就成为了关键窗口;而UIActionSheet则没有出现在任何窗口中——它被显示时主应用窗口仍然是关键窗口,并且actionSheet本身不是它的子视图。 - executor21
当视图控制器从堆栈中弹出时,视图确实会消失。它消失是因为UINavigationController将其从视图层次结构中移除。视图层次结构不是每次推送或弹出控制器时都会重新创建的临时内容。视图层次结构是在您向UIWindow添加第一个子视图时诞生的,通常是在applicationDidFinishLaunching:中完成。它在应用程序退出时结束。 - cduhn
@cdun -- 你说的一切都是真实的,但与讨论的重点无关。在一个设计良好的应用程序中,您可以忽略幕后的视图层次结构,因为应用程序绘制视图的方式与视图之间的逻辑关系无关。每个视图控制器的视图在其他视图方面完全独立。只要它们所代表的数据存在于数据模型中,您就应该能够以任何顺序加载它们,并且它们仍将正常工作。唯一关键的层次结构是视图控制器层次结构。开发人员可以并且应该忽略应用程序级别的视图层次结构。 - TechZen
我想补充一点,你只能通过打破MVC并将所有控制器逻辑塞进视图中来实现“你可以编写一个具有视图但没有视图控制器的应用程序”。语言和API允许这样做,但这是非常糟糕的设计。它破坏了封装性,并将每个视图都与其他视图联系起来,这会导致任何地方的微小更改都会破坏整个应用程序。这相当于使用goto语句而不是函数的现代OOP等价物。如果你认为视图是应用程序的逻辑核心,而视图控制器和数据模型是次要的,那么你将在未来面临巨大的困难。 - TechZen
显示剩余5条评论

0

UIView 上的以下方法可能是您需要的:

 didAddSubview:
 didMoveToSuperview
 didMoveToWindow
 willMoveToSuperview:
 willMoveToWindow:
 willRemoveSubview:

0

我现在对UITabBarViewController执行了相同类型的日志记录,因此考虑第5部分:

它似乎与UINavigationController以相同的方式工作:只有与当前活动选项卡相关联的视图控制器的视图位于keyWindow的视图层次结构中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接