一次关闭多个模态视图控制器时的视觉伪影

Posted

技术标签:

【中文标题】一次关闭多个模态视图控制器时的视觉伪影【英文标题】:Visual artifact when dismissing more than one modal view controller at once 【发布时间】:2015-07-02 22:02:11 【问题描述】:

我一直在努力寻找这个问题的答案。我通过以下方式构建了一堆模态:

[[[NavA viewControllers] objectAtIndex:0] presentViewController:NavB animated:YES completion:NULL];
[[[NavB viewControllers] objectAtIndex:0] presentViewController:NavC animated:YES completion:NULL];

当我想同时关闭 NavANavB 模态时,我会调用

[[[NavA viewControllers] objectAtIndex:0] dismissViewControllerAnimated:YES completion:NULL];

这很好用,只是在整个堆栈被解除时,您可以看到 NavB 的短暂闪烁。

我单步调试了调试器,看起来在动画开始之前 NavC 立即消失,NavB 以动画形式消失。

有什么方法可以避免这种视觉伪影并让整个堆栈平滑地消失,而 NavC 在整个动画期间可见?

编辑:为了澄清,我提出UINavigationController而不是UIViewController,因为此流程用于用户登录,并且有多个可能的分支可以返回当前阶段,例如NavC (LoginPage), NavB (LandingPage with login and signup button) or all way back to root, NavA (main page of the应用)。在 ios 文档中,他们展示了与相机类似的设计模式,其中每个阶段都展示了一个 UINavigationController 和多个可能的视图控制器 https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html

【问题讨论】:

我需要更好地理解这一点。你有 1 个UINavigationController,它有 3 个视图控制器,ABC,在堆栈上还是你有 3 个UINavigationControllers,每个都显示有-presentViewController:animated:completion: @JefferyThomas 后者 - NavB 和 NavC 是通过@987654333 呈现的 UINavigationControllers@NavA 是根视图控制器。 你在dismiss时是用NO还是YES来做动画? @BorisGafurov 我在解散时使用 YES 来制作动画 你试过NO吗?你应该只看到你的 A 视图出现 【参考方案1】:

实际上,无论您将其放在何处或如何称呼它,都无法仅使用dismissViewControllerAnimated:completion:方法来做到这一点(至少我不能,如果有人知道方法-我们都想知道)。


但是,您可以使用 hack 来实现您想要的结果(此代码应从“B”ViewController 调用):

// Snapshot of "C" ViewController
UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, YES, 0);
UIView *snapshot = [self.presentedViewController.view snapshotViewAfterScreenUpdates:NO];
UIGraphicsEndImageContext();

// Cover the entire view of "B" (and hide navigation bar)
[self.view addSubview:snapshot];
self.navigationController.navigationBarHidden = YES;

// Dismiss "C" without animation
[self.presentedViewController dismissViewControllerAnimated:NO completion:^
    // Dismiss "B" with animation
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
];

【讨论】:

小心这个解决方案。在 iOS 7 中,dismissViewControllerAnimated:NO 不会同步进行布局,并且当您调用第二次关闭时可能会崩溃,因为视图控制器仍被视为“中间过渡”。 @JohnEstropia 因为第一个没有动画,你认为它还会影响吗?那么在第一行的完成块中添加第二行会更好吗? 将第二个dismiss 移到第一个的完成中肯定会防止上述问题(尽管presentingViewController 可能会闪烁一瞬间)。我不知道它是否已在 iOS 8 上修复,但当时我们遇到了链式解雇的问题。【参考方案2】:

如果您使用的是故事板,那么这应该可以使用 Unwind Segues 来实现。 Mike Woelmer 对此有一个good set of articles。基本上,您向 Storyboard 提供有关视图如何通过多个不同视图展开以获得已在堆栈中的视图的信息。

但是,我有点困惑,就像 Jeffery Thomas 在 cmets 中一样:你为什么要展示一个导航控制器和另一个导航控制器?我可以理解您可能希望导航栏在不同视图上看起来不同,但您可以自定义视图何时出现。您应该考虑一下 NavB 和 NavC 中视图的内容,并问自己是否应该将它们呈现为模态视图,或者它们是否会更好地作为单个导航堆栈的一部分。通过以模态方式呈现每个导航控制器,您最终会得到多个导航堆栈,而不是具有多个视图控制器的单个堆栈。即使只有 NavB 和 NavC 是同一堆栈的一部分,它也可能会消除您看到的视觉故障。

如果您确实使用了单个导航控制器,那么您可以使用 UINavigationController 上的 -popToViewController:animated: 方法返回到导航堆栈中的前一个视图控制器。

如果您决定像当前那样以模态方式呈现 NavB 和 NavC 是正确的做法,那么您可能会遇到麻烦,因为当您要求 NavA 关闭其视图控制器时,它会尝试关闭 NavB,这会导致这意味着在 NavB 的视图和 NavA 的视图之间建立一个过渡。这就是为什么您会看到这种转变,而不是您想要的转变(在 NavC 的观点和 NavA 的观点之间)。一种可能有效(听起来有点奇怪)的方法是尝试从 NavC 呈现 NavA,然后覆盖转换以使其看起来像是从堆栈中弹出 NavC。到达那里后,您可以通过删除对 NavB 和 NavC 的任何强引用来清理内容。 This article from Ash Furrow 将助您一臂之力。

【讨论】:

不幸的是我没有使用故事板。由于需要返回每个阶段的多个分支,我正在展示导航控制器而不是视图控制器。作为一个实验,我只展示了 UIViewControllers 并最终得到了相同的故障动画。在此测试中,流程如下 [NavA viewControllers[0] present:VCB] -> [VCB present VCC] -> [NavA viewControllers[0] dismiss] 顺便谢谢你的回复,这让我重新审视了用户流程,我发现应用程序其他地方的主线程上没有执行一些演示。同样来自 Apple 文档,展示 Navigation Controllers 而不是 View Controllers 也是一种文档模式,他们提供了一个示例 developer.apple.com/library/ios/featuredarticles/… 对不起,我应该更清楚一点。问题不会出现,因为您使用的是 UINavigationControllers 而不是 UIViewControllers(正如您现在测试的那样)。我的意思是,如果您按照我们的意图使用 UINavigationController,即管理多个视图控制器的堆栈,那么一次弹出两个视图控制器的任务将变得微不足道,因为它们是相同的导航堆栈。嵌套模式视图的行为完全不同。 我也完全接受关于展示导航控制器的观点:如果它对您展示的内容有意义,这绝对没有错,我只是建议它可能需要重新检查。但是碰巧在您从 Apple 发布的链接中有一条线索:“当需要关闭呈现的视图控制器时,首选方法是让呈现的视图控制器关闭它。”在您的情况下,您试图将 NavC 从 NavA 中剔除,这不是提供它的 VC,这就是您的动画遇到问题的根本原因。【参考方案3】:

您可以将动画伪装成您希望的样子:

pop/dismiss B 和 C没有动画 推送/呈现 C 没有动画 使用任何你想要的动画弹出/关闭 C

【讨论】:

以上是关于一次关闭多个模态视图控制器时的视觉伪影的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 目前的模态视图控制器会导致一些动画伪影(iPhone)

视图控制器以模态方式呈现/关闭时的通知?

如何一次关闭 3 个模态视图控制器?

我怎样才能呈现一个模态视图控制器,并带有默认的关闭动画?

模态视图不会关闭

使用多个导航控制器关闭多个模式视图