模态视图控制器和子视图控制器如何交互?

5
我有一个自定义的容器视图控制器: ContainerVC。它的职责是根据当前屏幕方向(虽然我认为容器选择其视图无所谓)来展示两个内容视图控制器之一: ContentPortraitVCContentLandscapeVCContentPortraitVC在某个时候会弹出ContentModalDetailVC

因此这里有两种不同的显示新内容的方法:

  • 父子关系(通过addChildViewController启动,通过removeFromParentViewController移除),

  • 呈现和被呈现的关系(通过presentViewController启动,通过dismissViewController移除)。

如果ContainerVC添加了ContentPortraitVC,然后再呈现ContentModalDetailVC,此时ContainerVC决定切换到ContentLandscapeVC,则ContentModalDetailVC将保持可见状态(为什么其父视图被移除时它没有被移除?)

但是,当ContentPortraitVC被要求移除ContentModalDetailVC时,什么也不会发生。模态显示仍然保持不变。发生了什么?

2个回答

5
  1. 当你使用addChildViewController添加ContentPortraitVC时:

    a. ContentPortraitVCparentViewController属性被设置。

    b. 然后(根据Apple文档),您必须手动显示ContentPortraitVC的视图。如果按照文档操作,您需要将其作为ControllerVC的顶层视图的子项添加。

  2. 然后,ContentPortraitVC调用presentViewController来显示ContentModalDetailVC

    a. 这将设置它的presentingViewController属性(在调试器中,这显示为_parentModalViewController ivar -- 请注意,ivar与属性不同),并设置ContentPortraitVCpresentedModalViewController属性(它的ivar是_childModalViewcontroller)。

    b. 在iPhone上,ContentModalDetailVC的视图将完全替换ContentPortraitVCContainerVC的视图,因此只有模态视图控制器的视图可见。(在iPad上,它会将新的UI层叠在顶部,但作为ControllerVC视图的同级,而ControllerVC视图是ContentPortraitVC视图的父级)。

  3. 现在,您要从ContentPortraitVC过渡到ContentLandscapeVC

    a. IOS进行了一些魔法。它知道您正在删除的东西(ContentPortraitVC)当前有一个presentedViewController处于活动状态,因此它更改了其父项。它在ContentPortraitVC上将值设置为nil,获取子项(ContentModalDetailVC)并将其父项设置为新视图(ContentLandscapeVC)。因此,呈现模态视图的视图控制器不再是其呈现视图控制器。就好像ContentLandscapeVC最初呈现它一样!

    b. 在视图方面,您按照Apple文档更改ContentPortraitVCContentLandscapeVC的视图。但是,您只是更改了ControllerVC视图的子视图。在iPhone上,模态视图控制器仍然是唯一显示的内容,因此更改不会在屏幕上做任何事情。在iPad上,它确实会(尽管您可能看不到,因为模态视图通常是全屏的)。

  4. 现在,您要解除模态视图。可能是在ContentPortraitVC中执行此操作,但它不再引用任何它呈现的东西。因此,调用[self dismissViewController...无效,因为ContentPortraitVC不再呈现任何内容,该责任已转移到ContentLandscapeVC

那么这就是发生的事情和原因。以下是对此的解决方法。
1. 当您从ContentPortraitVC切换到ContentLandscapeVC时,您可以手动重新连接委托,以便后者尝试关闭模态控制器。 2. 您可以使用[self dismissModalControllerAnimated:YES completion:nil]来让模态控制器自行关闭。如果这看起来很奇怪,我将在另一个问题上提出并回答为什么会这样(IOS如何知道要关闭哪个?)。 3. 您可以使ControllerVC弹出模态视图,并负责删除它。

我遇到了这个问题,并从这个答案中理解了发生了什么,谢谢!那么,在切换时如何处理容器视图控制器中呈现的视图控制器的情况呢?即,在当前子视图控制器上检查presentedViewController并交换该视图。这会破坏设计吗? - stefanlindbohm

2
如果你检查ContentModalDetailVC上的presentingViewController,你会发现它实际上由ContainerVC而不是ContentPortraitVC呈现。
要解决这个问题,你只需要在ContentPortraitVC上设置definesPresentationContext(或在Interface Builder中使用“定义上下文”复选框)即可。
这将告诉ContentPortraitVC处理模态呈现,而不是将其传递给定义演示文稿上下文的下一个视图控制器(默认情况下为根视图控制器)。
为了避免出现同样的问题,你可能还想让ContentLandscapeVC定义上下文。
当两个子控制器都定义自己的演示文稿上下文时,当ContainerVC决定交换子控制器时,任何模态呈现都将从新层次结构中移除,包括呈现它的子控制器。无需在交换之前尝试进行hacky操作来关闭模态呈现 :)
编辑:我应该补充说明,被呈现的视图控制器必须将其modalPresentationStyle设置为currentContextoverCurrentContext

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