addChildViewController实际做什么?


102

我只是第一次涉足iOS开发,而我要做的第一件事就是实现一个自定义容器视图控制器 -让我们称之为它SideBarViewController-交换掉它可能使用的几个子视图控制器中的一个显示,几乎完全像标准的Tab Bar Controller一样。(它几乎是一个标签栏控制器,但具有可隐藏的侧菜单而不是标签栏。)

按照Apple文档中的说明,addChildViewController每当将子ViewController添加到容器中时,我都会调用。我的代码换出了当前的子视图控制器,如下所示SideBarViewController

- (void)showViewController:(UIViewController *)newViewController {
    UIViewController* oldViewController = [self.childViewControllers 
                                           objectAtIndex:0];
    
    [oldViewController removeFromParentViewController];
    [oldViewController.view removeFromSuperview];
    
    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self addChildViewController: newViewController];
    [self.view addSubview: newViewController.view];
}

然后,我开始尝试弄清addChildViewController这里所做的事情,然后我意识到自己不知道。除了坚持新ViewController的在.childViewControllers阵,它似乎对任何事情没有影响。即使我从未调用过addChildViewController,从子控制器的视图到我在情节提要上设置的子控制器的操作和输出仍然可以正常工作,而且我无法想象它还会影响什么。

确实,如果我将代码重写为not call addChildViewController,而是看起来像这样……

- (void)showViewController:(UIViewController *)newViewController {

    // Get the current child from a member variable of `SideBarViewController`
    UIViewController* oldViewController = currentChildViewController;

    [oldViewController.view removeFromSuperview];

    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self.view addSubview: newViewController.view];

    currentChildViewController = newViewController;
}

...据我所知,我的应用仍然可以完美运行!

Apple文档并没有太多说明做什么,addChildViewController也没有说明我们为什么要称呼它。目前,有关该方法的用途或为什么应在“ UIViewController类参考”的其部分中使用该方法的相关描述的整个范围是:

将给定的视图控制器添加为子级。...此方法仅旨在由自定义容器视图控制器的实现调用。如果重写此方法,则必须在实现中调用super。

同一页面的前面也有此段落:

您的容器视图控制器必须将子视图控制器与其自身关联,然后才能将子视图的根视图添加到视图层次结构中。这样,iOS可以将事件正确地路由到子视图控制器以及这些控制器管理的视图。同样,在从其子视图层次结构中删除子视图的根视图之后,它应将该子视图控制器与其自身断开连接。为了建立或破坏这些关联,您的容器调用由基类定义的特定方法。容器类的客户端不希望调用这些方法;它们只能由您的容器的实现用于提供预期的容纳行为。

这是您可能需要调用的基本方法:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

但是它并没有提供任何线索来说明它所谈论的“事件”或“预期遏制行为”是什么,或者为什么(甚至何时)调用这些方法是“必要的”。

Apple文档的“自定义容器视图控制器”部分中的自定义容器视图控制器的示例都调用了此方法,因此我认为它具有一些重要的作用,而不仅仅是将子ViewController弹出到数组中,但是我无法确定找出目的是什么。此方法有什么作用,为什么要调用它?


3
Apple的2011年WWDC视频页面上有一个关于此主题的精彩会议(“实现UIViewController包含”)。
Alladinian 2013年

Answers:


94

我也想知道这个问题。我观看了WWDC 2011的第102部分视频,而View Controller先生Bruce D. Nilo则这样说:

viewWillAppear:viewDidAppear:等与无关addChildViewController:。所addChildViewController:要做的就是说“此视图控制器是那个视图的一个孩子”,它与视图外观无关。它们何时被调用与视图何时移入和移出窗口层次结构相关联。

因此,似乎对的调用addChildViewController:作用很小。通话的副作用是重要的部分。它们来自parentViewControllerchildViewControllers关系。这是我知道的一些副作用:

  • 将外观方法转发给子视图控制器
  • 转发旋转方式
  • (可能)转发内存警告
  • 避免不一致的VC层次结构,尤其是在transitionFromViewController:toViewController:…两个VC需要具有相同父代的情况下
  • 允许自定义容器视图控制器参与状态保存和恢复
  • 参与响应者链
  • 挂钩的navigationControllertabBarController等属性

这是会议102而不是101
SeanChense

响应者链+1。如果要在子UIViewController拥有的子视图上接收触摸事件,则需要addChildViewController
charlieb

108

我认为一个例子值得一千个字。

我正在开发图书馆应用程序,想要显示一个漂亮的记事本视图,该视图在用户想要添加注释时出现。

在此处输入图片说明

在尝试了一些解决方案之后,我最终发明了自己的自定义解决方案以显示记事本。因此,当我想显示记事本时,我创建的新实例NotepadViewController并将其根视图作为子视图添加到主视图。到目前为止,一切都很好。

然后我注意到记事本图像在横向模式下部分隐藏在键盘下。

在此处输入图片说明

所以我想更改记事本图像并将其上移。为此,我在willAnimateRotationToInterfaceOrientation:duration:方法中编写了正确的代码,但是当我运行该应用程序时,什么也没发生!在调试之后,我注意到UIViewController实际上没有调用任何旋转方法NotepadViewController。仅在主视图控制器中的那些方法被调用。

为了解决这个问题,我需要从中调用所有方法 NotepadViewController在主视图控制器中手动。这很快会使事情变得复杂,并在应用程序中不相关的组件之间产生额外的依赖关系。

那是过去,在引入子视图控制器概念之前。但是现在,您只需要 addChildViewController使用主视图控制器,一切就可以按预期工作,而无需进行任何手动工作。

编辑: 有两类事件被转发到子视图控制器:

1-外观方法:

- viewWillAppear:
- viewDidAppear:
- viewWillDisappear:
- viewDidDisappear:

2-旋转方式:

- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:

您还可以通过覆盖shouldAutomaticallyForwardRotationMethods和来控制要自动转发的事件类别shouldAutomaticallyForwardAppearanceMethods


从文档中进行快速测试后,我认为没有其他事件仅在您addChildViewController转到父控制器时才转发。
Hejazi 2013年

希望它能自动转发viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]仅将传入的视图控制器添加到viewController(父级)要保留其引用的viewControllers数组中。实际上,您应该自己在屏幕上添加这些viewController的视图,方法是将它们添加为另一个视图的子视图(例如parentViewController的视图)。Interface Builder中还有一个方便对象,可以在Storyboard中使用childrenViewControllers。

以前,要保留使用了其视图的其他viewController的引用,必须将其手动引用保留在@properties中。有一个内置的财产一样childViewControllers,因此parentViewController是管理这种互动和构建组成viewControllers像UISplitViewController你的iPad应用程式找到一种方便的方式。

此外,childrenViewControllers还自动接收父级收到的所有系统事件:-viewWillAppear,-viewWillDisappear等。以前,您应该在“ childrenViewControllers”上手动调用此方法。

而已。


您认为这就是全部的依据是什么?另外,您能否提供孩子收到的“系统事件”列表?Google搜索iOS "system events"不会增加太多;这似乎不是苹果使用的术语?
Mark Amery 2013年

从本质上讲,这是一种便捷的方法,它允许您将View Controller B的视图添加为View Controller A的子视图,但仍然让View Controller B管理其视图。为了使其正常工作,您需要确保View Controller B正在获取系统事件(读取UIViewControllerDelegate回调)。'addChildViewController'为您完成了此操作,从而节省了手动传递所有内容的工作量。
Sam Clewlow13年
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.