一次性关闭多个模态视图控制器时出现了视觉伪影问题

9

我一直在努力寻找答案。我通过以下方式建立了一个模态框的堆栈:

[[[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)或完全回到根目录, NavA (应用程序的主页面)。在iOS文档中,他们使用相似的设计模式来展示相机,其中每个阶段都呈现具有多个可能的视图控制器的 UINavigationController https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html


我需要更好地理解这个问题。你是在一个 UINavigationController 中有三个视图控制器 ABC,还是有三个 UINavigationController 分别通过 -presentViewController:animated:completion: 呈现? - Jeffery Thomas
@JefferyThomas, 后者 - NavB 和 NavC 通过 [self presentViewController: navController animated: TRUE completion:^ {}]; 呈现为 UINavigationControllers。NavA 是根视图控制器。 - alexgophermix
当你关闭动画时,你使用“否”还是“是”? - Boris Gafurov
@BorisGafurov 当我取消时,我使用YES进行动画。 - alexgophermix
你试过NO吗?你应该只能看到你的A视图显示出来。 - Boris Gafurov
3个回答

4

实际上,无论您将dismissViewControllerAnimated:completion:方法放在哪里或如何调用它(至少我无法做到),都没有办法仅使用该方法来完成目标(如果有人知道方法-我们都想知道)。


然而,有一个技巧可以用来实现您想要的结果(此代码应从“B”视图控制器中调用):

// 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];
}];

1
请注意这个解决方案。在iOS 7中,dismissViewControllerAnimated:NO不会同步进行布局,并且当您调用第二次dismiss时,视图控制器仍被视为“中间转换”,可能会导致崩溃。 - John Estropia
@JohnEstropia,既然第一个没有动画,你认为它仍会产生影响吗?是不是最好将第二行代码直接添加到第一个完成块中呢? - Islam
将第二个dismiss移动到第一个的完成处理程序中,肯定会防止上述问题(尽管presentingViewController可能会闪烁一瞬间)。我不知道它是否在iOS 8上修复了,但我们当时遇到了链接解除的问题。 - John Estropia

3
如果您正在使用Storyboard,则可以使用Unwind Segues实现此目标。Mike Woelmer有一篇关于此的好文章。基本上,您需要向Storyboard提供关于如何通过多个不同的视图取消到达已经在堆栈中的视图的信息。
然而,我有点困惑,就像Jeffery Thomas在评论中所说的那样:为什么要使用另一个导航控制器来呈现导航控制器?我可以理解您可能希望导航栏在不同的视图上看起来不同,但是当视图即将出现时,您可以进行自定义。您应该思考NavB和NavC视图的内容,并问自己它们是否应该作为模态视图呈现,还是作为单个导航堆栈的一部分更好。通过以模态方式呈现每个导航控制器,您最终会得到多个导航堆栈,而不是具有多个视图控制器的单个堆栈。即使只有NavB和NavC是同一堆栈的一部分,这也可能会消除您看到的视觉故障。

如果您使用单个导航控制器,则可以使用UINavigationController上的方法-popToViewController:animated:返回导航堆栈中的先前视图控制器。

如果您决定像目前一样以模态方式呈现NavB和NavC,则可能会遇到麻烦,因为当您要求NavA解除其视图控制器时,它将尝试解除NavB,这对它来说意味着在NavB的视图和NavA的视图之间设置一个转换。这就是为什么您看到该转换而不是您想要的转换(即在NavC的视图和NavA之间)。可能有效的一种方法(听起来有点奇怪)是尝试从NavC呈现NavA,然后覆盖转换以使其看起来像您正在弹出NavC。到达那里后,您可以通过删除对NavB和NavC的任何强引用来清理事情。Ash Furrow的这篇文章将帮助您完成大部分工作。


很遗憾,我没有使用故事板。我呈现导航控制器而不是视图控制器,因为有多个分支需要返回到每个阶段。作为一项实验,我只呈现了UIViewControllers,最终得到了相同的动画问题。在这个测试中,流程如下所示[NavA viewControllers [0] present:VCB] -> [VCB present VCC] -> [NavA viewControllers [0] dismiss]。 - alexgophermix
1
顺便感谢你的回复,它让我重新审视了用户流程,并发现应用程序其他地方的一些演示没有在主线程上执行。此外,根据苹果文档,使用导航控制器而不是视图控制器进行呈现也是一种文档模式,他们提供了一个例子:https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html - alexgophermix
抱歉,我应该对此更加清晰明确。问题并不是因为您使用UINavigationController而不是UIViewController(正如您现在测试的那样)。我的意思是,如果您按照其预期使用UINavigationController来管理多个视图控制器的堆栈,那么一次弹出两个视图控制器的任务将变得微不足道,因为它们是同一个导航堆栈的一部分。嵌套模态视图的行为完全不同。 - quantumkid
我也完全同意关于呈现导航控制器的观点:如果它对你要呈现的内容有意义,那么这样做绝对没有问题,我只是建议可能需要重新审视一下。但恰好在你发布的苹果链接中有一个提示:“当该呈现的视图控制器需要被解散时,首选方法是让呈现视图控制器来解散它。” 在你的情况下,你正在尝试从NavA中解散NavC,而NavA并不是呈现它的VC,这就是你的动画遇到问题的根本原因。 - quantumkid

2

您可以伪造动画,使其看起来与您想要的完全一样:

  • 弹出/关闭 B 和 C无需动画
  • 推送/呈现 C无需动画
  • 使用任何您希望的动画来弹出/关闭 C

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