addChildViewController实际上做了什么?

我只是第一次进入iOS开发,我不得不做的第一件事就是实现一个自定义的容器视图控制器 – 让我们调用它的SideBarViewController – 交换了几个可能的子视图控制器它显示,几乎完全像一个标准的标签栏控制器 。 (这几乎是一个标签栏控制器,但有一个可隐藏的侧面菜单,而不是一个标签栏。)

按照Apple文档中的说明,每当向容器中添加一个子ViewController时,我都会调用addChildViewController 。 我用于交换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在这里做了什么,我意识到我不知道。 除了在.childViewControllers数组中粘贴新的ViewController .childViewControllers ,它对任何东西都没有影响。 从子控制器的视图到我在故事板上设置的子控制器的操作和插口仍然可以正常工作,即使我从不调用addChildViewController ,也无法想象会有什么影响。

事实上,如果我重写我的代码不调用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; } 

…那么我的应用程序仍然完美,只要我可以告诉!

苹果的文档并没有说明addChildViewControllerfunction,或者为什么我们要调用它。 目前,在UIViewController类参考中,该方法的相关描述的全部范围或为什么应该在其部分中使用该范围 :

添加给定的视图控制器作为一个孩子。 …此方法仅用于通过自定义容器视图控制器的实现进行调用。 如果您重写此方法,则必须在实现中调用super。

在同一页面上也有这个段落:

在将子视图添加到视图层次结构之前,您的容器视图控制器必须将子视图控制器与其自身相关联。 这允许iOS将事件正确地路由到子视图控制器以及这些控制器pipe理的视图。 同样,在从其视图层次结构中删除一个孩子的根视图之后,它应该将该子视图控制器与其自身断开连接。 为了build立或closures这些关联,你的容器调用由基类定义的特定方法。 这些方法不打算由您的容器类的客户端调用; 它们只能在您的容器的实现中使用,以提供预期的控制行为。

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

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

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

Apple文档的“Custom Container View Controllers”部分中的自定义容器视图控制器的示例都调用此方法,所以我认为除了将子ViewControllerpopup到数组上之外,它还有一些重要的用途,但是我无法出于什么目的 这个方法是做什么的,为什么要这么叫呢?

我也在想这个问题。 我观看了WWDC 2011video的第102场会议,视图控制员Bruce D. Nilo先生说:

viewWillAppear:viewDidAppear:等与addChildViewController:无关addChildViewController: 。 所有addChildViewController:做的是说“这个视图控制器是一个孩子”,它与视图外观没有任何关系。 当它们被调用的时候,视图移入和移出窗口层次结构。

所以看来,调用addChildViewController:做的很less。 呼叫的副作用是重要的一部分。 他们来自parentViewControllerchildViewControllers关系。 以下是我所知道的一些副作用:

  • 将外观方法转发到子视图控制器
  • 转发旋转方法
  • (可能)转发内存警告
  • 避免不一致的VC层次结构,特别是在transitionFromViewController:toViewController:…两个VC都需要有相同的父级
  • 允许自定义容器视图控制器参与状态保存和恢复
  • 参与响应者链
  • 连接navigationControllertabBarController等属性

我认为一个例子胜过千言万语。

我正在研究一个图书馆应用程序,并希望显示一个不错的记事本视图,当用户想要添加一个笔记时出现。

在这里输入图像说明

尝试了一些解决scheme之后,我最终创造了自己的定制解决scheme来显示记事本。 所以,当我想要显示记事本时,我创build了一个NotepadViewController的新实例,并将它的根视图作为子视图添加到主视图中。 到现在为止还挺好。

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

在这里输入图像说明

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

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

在引入子视图控制器的概念之前,这是过去的事情。 但是现在,您只需要将addChildViewController到主视图控制器,一切都将按预期方式工作,无需任何手动工作。

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

1-外观方法:

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

2-旋转方法:

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

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

-[UIViewController addChildViewController:]只添加一个viewController(父级)想要引用的viewControllers数组中传入的视图控制器。 实际上你应该在屏幕上添加这些viewController的视图,把它们作为另一个视图的子视图(例如parentViewController的视图)来添加。 在Interface Builder中还有一个方便的对象来使用Storyboard中的childrenViewControllers。

以前,为了保持其他viewController的引用,你必须在@properties中手工引用它们。 拥有像childViewControllersparentViewController这样的childViewControllers属性是一种便捷的方式来pipe理这种交互,并构build组合viewController,比如您在iPad应用上find的UISplitViewController。

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

而已。