UIViewController旋转方法

17

有哪个对象负责分发UIViewController旋转方法调用,例如:

  • shouldAutorotateToInterfaceOrientation:
  • willRotateToInterfaceOrientation:duration:
  • willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
  • willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:
  • didRotateFromInterfaceOrientation:

我认为它是UIApplication(但也可能是AppDelegate或UIWindow)。

下一个问题是,这个对象如何知道要与哪个UIViewController通信?

它如何知道哪个UIViewController将其视图作为窗口的子视图?

是否可以发送消息或设置属性(某些对象的属性),以设置应用程序的“活动”UIViewController


好问题,这几天我一直在思考这个问题。我正要问类似的问题,结果碰巧看到了这个! - Raj Pawan Gumdal
我应该像你一样提问。这些都是与通用框架相关的问题,“自定义框架,阅读文档,好的,现在我应该如何知道它应该如何工作”。 - Henrik Erlandsson
4个回答

13

看起来UIApplication正在向活动视图控制器分派消息。

但是你的视图控制器实例如何接收这些消息呢?

该消息会转发到第一个将其视图添加到UIWindow实例中的视图控制器。

这可以归结为3种基本情况:

  1. 直接将其视图添加到UIWindow实例(单个视图应用程序)的ViewController

  2. 在导航应用程序中的导航控制器,然后导航控制器将消息转发给活动视图的视图控制器。

  3. 在选项卡应用程序中的选项卡栏控制器,然后选项卡栏控制器将消息转发给活动视图的视图控制器(或活动导航控制器)。

如果您构建了一个具有多个视图但未使用导航控制器或选项卡栏控制器的应用程序,则可能会出现问题。 如果手动在UIWindow实例中切换视图,您将无法可靠地接收到这些消息。 这类似于iPhone viewWillAppear not firing之类的帖子。

只需使用苹果的多视图约定即可。 希望这可以为某些人节省一两个小时的时间。


有一个疑问,导航控制器/标签栏控制器是否会将消息传递给子视图的控制器,还是只调整视图大小?我认为它们只调整其子视图的大小,第一个视图控制器不会向下传递消息到其视图层次结构,它们只会调整其子视图大小。 - Raj Pawan Gumdal
准确地说,苹果建议每个屏幕只使用一个视图控制器。因此,不支持使用“子”视图控制器,并且消息不会自动转发。 - Corey Floyd
Corey,当你有一个应用程序在同一屏幕上需要两个表视图时,如何限制自己只使用一个视图控制器呢?你需要为每个表视图创建一个VC,然后再创建一个VC来管理它们。或者考虑另一个例子:一个应用程序需要类似于选项卡栏的行为,但由于图形/艺术要求需要自定义选项卡栏控制器?对于复杂的UI而言,仅使用一个VC似乎是不可行的目标。在这些情况下,必须有*某种方法来可靠地控制接口旋转事件吧? - Yetanotherjosh
在我的测试中,我发现当嵌套具有不同视图控制器的视图时,有时会任意地决定哪个视图控制器接收 shouldAutorotateToInterfaceOrientation 消息 - 它绝对 不是 总是发送到管理 UIWindow 层次结构中最高子视图的视图控制器 - 它肯定可以发送到相应于该视图控制器视图的子级的视图控制器。 - Yetanotherjosh

4
它是如何知道哪个UIViewController的视图作为窗口的子视图呢?
UIViewController类维护着一个静态映射表,将视图和它们的视图控制器对应起来。在CocoaTouch中的一些关键位置会查询这个映射表。特别地,[UIView nextResponder]会查询这个映射表,并在找到控制器时返回它。
我猜UIWindow也会通过查找它的根视图来确定要将旋转事件转发给哪个控制器。下次我在反汇编中查找某些内容时,会检查一下这一点。(我花了一些时间进行CocoaTouch的逆向工程,以了解其中的工作原理。)

4

这都是神奇的事情,您无需担心。只需要将您的控制器根视图添加到窗口中 - 或者让选项卡栏或导航控制器为您完成 - 它就会接收到这些消息。

(但是如果您使用调试器查看一下,您可能会得出我得出的结论:有一种内部表格将每个控制器的视图映射回控制器,并且基于该链接分派消息。)


更新:当我第一次撰写此答案时,这确实是私有魔法,但随后对iOS进行的更改使其背后的API公开。现在可以通过 UIWindow.rootViewController 属性访问根视图控制器,并且使用 UIViewController.childViewControllers 属性形成子视图控制器的树形结构。

父视图控制器负责通知其子视图控制器方向的更改。(我不确定如何通知根视图控制器,但您可以设置断点并自行查找。)-shouldAutomaticallyForwardRotationMethods 方法决定 UIViewController 是否会为您执行此操作。如果返回 NO,则您需要在您的 -willRotateToInterfaceOrientation:duration:-willAnimateRotationToInterfaceOrientation:duration:-didRotateFromInterfaceOrientation: 方法中负责执行此操作。


真的很神奇,+1。根视图控制器会根据自动调整大小的掩码旋转和调整大小,并且子视图的框架也会被改变和调整大小。不知道为什么有人会对这位可怜的家伙投反对票! - Raj Pawan Gumdal
+1,我不认为有理由对这个答案进行负评。很遗憾的是,人们不说出他们为什么要对一个答案进行负评。 - Piotr Czapla
那么当“魔法”不起作用时怎么办呢?我有一个应用程序无法发布,因为didRotateFromInterfcaeOrientation:从未被调用,自动调整大小的掩码无法将视图定位到正确的位置。这是一个完全合理的担忧,这个问题中的任何答案都没有解决我的问题 - 所以显然还有更多需要考虑的东西,而不仅仅是“让选项卡栏为您完成它”。 - Abhi Beckert
@AbhiBeckert 我几年前给出了这个答案,当时没有公共API用于查看控制器层次结构。现在这个API已经可以供任何人使用了。请查看UIViewController.childViewControllers和其他控制器包含API。它们可能会帮助你找到你要找的东西。 - Becca Royal-Gordon

1

我有一个类似的问题。

我有一个在横向模式下工作的游戏(左右横向都可以)。

为了管理多视图应用程序,我使用了一个根UIviewController,即一个没有任何功能的虚拟UIViewcontroller和一个UIview。然后我将每个其他UIview添加到它作为子视图。

当启动应用程序时,仿真器会快速旋转到纵向方向,并且我得到每个视图的frame属性看起来像(0,-80,320,480)。这导致视图被右侧的160 X 320矩形剪裁。

我注意到每个子视图的-shouldAutorotateToInterfaceOrientation:方法只调用一次,而且发生在它被添加到根视图时。后来,在旋转设备时,只有根视图才会调用它的方法。

毫不奇怪,任何一个-willAnimate*方法调用都只发送到根视图,因此,在子视图中定义任何这些方法都是无意义的。

这些方法在每次方向更改为-shouldAutorotateToInterfaceOrientation:方法接受的方向时都会被调用。所以我想我可以从那里改变我的子视图的frame属性。

为了实现这一点,我在我的rootViewController类中定义了以下方法:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    subview1_ViewController.view.frame = CGRectMake(0,0,480,320);
    subview2_ViewController.view.frame = CGRectMake(0,0,480,320);
        ...
}

我不明白为什么框架属性没有更新,但我知道这个解决方法可行。

希望这可以帮到你...


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