当前位置: 首页 > 知识库问答 >
问题:

addChildViewController实际上做什么?

戚永福
2023-03-14

我只是第一次涉足iOS开发,我必须做的第一件事就是实现一个自定义容器视图控制器——让我们称之为SideBarViewController——它交换了几种可能的视图控制器中的哪一种它显示的子视图控制器,几乎与标准的Tab Bar Controller一模一样。(它几乎是一个标签栏控制器,但有一个可隐藏的侧菜单,而不是标签栏。)

根据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在这里做什么,我意识到我不知道。除了将新的ViewController粘贴在. ChilViewController数组中之外,它似乎对任何东西都没有影响。即使我从未调用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;
}

... 那么,就我所知,我的应用程序仍然可以完美运行!

苹果的文档并没有对addChildViewController的功能以及我们为什么要称之为它给出太多的解释。目前,UIViewController类参考中关于该方法的功能或使用原因的相关描述的完整范围为:

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

同一页前面还有一段:

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

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

addChildViewController:
从ParentViewController中移除
willMoveToParentViewController:
didMoveToParentViewController:

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

Apple留档的"Custom Container View Controller"部分中的自定义容器视图控制器的示例都调用此方法,所以我假设它除了将子ViewController弹出到数组之外还具有一些重要的目的,但我不知道该目的是什么是。这种方法是做什么的,我为什么要称之为?

共有3个答案

濮泳
2023-03-14

-[UIViewController addChildViewController:]仅将传递的视图控制器添加到视图控制器数组中,该视图控制器(父级)希望保留引用。实际上,您应该在屏幕上自己添加这些view Controller视图,将它们添加为另一个视图的子视图(例如,父母视图控制器视图)。接口生成器中还有一个方便的对象,可以在故事板中使用儿童视图控制器。

以前,要保留对使用其视图的其他视图控制器的引用,必须在@属性中保留对它们的手动引用。有一个内置属性,如子视图控制,因此父母视图控制器是一种方便的方法来管理这样的交互和构建组成的视图控制器,如您在iPad应用程序上找到的UISplitViewController。

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

就这样了。

郑衡
2023-03-14

我也在想这个问题。我观看了WWDC 2011年第102期的视频,视图管理员Bruce D.Nilo先生说:

视图将出现:视图显示:,等等与addChildViewController:无关。addChildViewController:所做的只是说“此视图控制器是该视图控制器的子视图”,而与视图外观无关。调用时与视图移入和移出窗口层次结构时相关联。

因此,调用addChildViewController:似乎做的很少。电话的副作用是重要的一部分。它们来自父母视图控制器儿童视图控制器关系。以下是我知道的一些副作用:

  • 将外观方法转发到子视图控制器
姬飞昂
2023-03-14

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

我正在开发一个库应用程序,希望在用户想要添加便笺时显示一个漂亮的记事本视图。

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

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

所以我想改变记事本的图像并将其向上移动。要做到这一点,我写了适当的代码在will AnimateRotationToInterfaceOrision:持续时间:方法,但当我运行的应用程序什么都没发生!调试后,我注意到没有一个UIViewController的旋转方法实际上是在NotepadViewController中调用的。仅调用主视图控制器中的那些方法。

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

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

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

1-外观方法:

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

2-旋转方法:

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

您还可以通过重写应该自动转发的事件类别和应该自动转发的事件类别。

 类似资料:
  • 问题内容: 什么是真正做? 它的定义是 刷新实体时,此字段中保存的所有实体也会刷新 但这实际上意味着什么?有人可以给我一个简单的例子吗? 问题答案: 单独的CascadeType描述可能会有些混乱,但是有一种简单的方法可以从一般情况中找出来。 对于任何值,这意味着如果使用接口在实例上调用了操作,并且该实例具有对其他实体实例的引用,并且已经定义了关联,则该操作也将应用于该关联实体。 因此定义为: 从

  • 我正试图把我的头缠在Apache Mesos上,需要澄清几个项目。 我对Mesos的理解是,它是一个安装在集群中的每个物理/VM服务器(“节点”)上的可执行文件,然后提供一个Java API(不知何故),将每个单独的节点视为计算资源(CPU/RAM/等)的集体池。因此,对于使用Java API编码的程序,他们只看到一组资源,而不必担心如何/在哪里部署代码。 因此,首先,我在这里的理解可能是根本错误

  • 从文档中可以看出,-Xss用于设置JVM的堆栈大小。但我对这一说法感到非常困惑。 在Java中,每个线程都有自己的堆栈。-Xss指定的数字是否: > 线程的每个堆栈的大小。e、 如果将-Xss设置为256K,则每个线程将有一个256K大的堆栈。因此,10个线程将总共使用2560K。 非常感谢你。 编辑: 谢谢你的回答。看起来像是上面的(2)senario-Xss指定特定线程的最大堆栈大小。 然后我

  • 我在看一个典型的for循环: 我对int I=1后的分号很满意:它是一个声明新变量的语句。如果i也是一个语句,为什么后面没有分号? 另一个例子。我打开Jshell并放置以下内容: 换句话说,命令可以工作,与是否有分号无关。我希望没有它就无法工作。 最后一个示例(改编自关于

  • 我已经回答了一个关于Python中的绝对导入的问题,我认为通过阅读Python2.5更改日志和附带的PEP我理解了这个问题。但是,在安装Python2.5并尝试创建一个从__future__import absolute_import中正确使用

  • 问题内容: 我是Spark-DataFrame API的初学者。 我使用此代码将制表符分隔的csv加载到Spark Dataframe中 假设我从新文件中使用Spark创建了DataFrame,然后使用内置方法toPandas()将其转换为熊猫, 是否将Pandas对象存储到本地内存? 熊猫的低级计算是否全部由Spark处理? 它是否公开了所有熊猫数据框功能?(我想是的) 我可以将它转换为熊猫并完