如何一次关闭 3 个模态视图控制器?
Posted
技术标签:
【中文标题】如何一次关闭 3 个模态视图控制器?【英文标题】:How to dismiss 3 modal view controllers at once? 【发布时间】:2016-01-01 14:43:09 【问题描述】:我有一个应用程序,它有一个初始登录屏幕,然后当用户想要注册时,他们会看到一个注册表单,该注册表单是三个视图控制器以模态方式呈现的。当用户在第三个屏幕上完成表单时(通过按“完成”按钮),我希望用户返回初始登录屏幕。
我已经尝试在第三个视图控制器中这样做:
[self dismissViewControllerAnimated:NO completion:nil]
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil]
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:NO completion:nil]
但是它只关闭了两个视图控制器,而不是全部 3。为什么会发生这种情况?
【问题讨论】:
你为什么要使用这么多模态?为什么不是一个带包控制器的模态? 我正在使用自定义导航控制器,但我真的不知道什么是包控制器 错字,从导航自动更正(即导航控制器)。所以推入导航控制器而不是使用许多模式。 我拥有的自定义导航控制器没有导航控制器。我正在使用这个(www.materialkit.io) 好吧,你应该看看一种重组方法来移除多个模态 【参考方案1】:正如其他人指出的那样,从用户体验的角度来看,有更优雅/高效/更简单的方法可以实现类似的结果:通过导航控制器、页面视图控制器或其他容器。
简短/快速回答:您需要在呈现视图控制器的链中更进一步,因为解除请求需要发送到正在呈现的控制器,而不是正在呈现的控制器。而且您只能将关闭请求发送到该控制器,它会负责从堆栈中弹出子控制器。
UIViewController *ctrl = self.presentingViewController.presentingViewController.presentingViewController;
[ctrl dismissViewControllerAnimated:NO completion:nil]
为了解释原因,并希望帮助其他人更好地理解 ios 中的控制器呈现逻辑,您可以在下面找到更多详细信息。
让我们从Apple documentation开始dismissViewControllerAnimated:completion:
关闭视图控制器以模态方式呈现的视图控制器。
呈现视图控制器负责关闭它呈现的视图控制器。如果您在呈现的视图控制器本身上调用此方法,UIKit 会要求呈现的视图控制器处理解除。
因此[self dismissViewControllerAnimated:NO completion:nil]
只是将请求转发给self.presentingViewController
。这意味着前两行具有相同的效果(实际上第二行什么也没做,因为在执行第一行之后没有提供控制器)。
这就是为什么您解雇视图控制器仅对前 2 个有效的原因。您应该从self.presentingViewController
开始,然后沿着呈现视图控制器的链前进。但这不是很优雅,如果以后视图控制器的层次结构发生变化,可能会导致问题。
继续阅读文档,我们偶然发现:
如果您连续呈现多个视图控制器,从而构建一个呈现的视图控制器堆栈,则在堆栈中较低的视图控制器上调用此方法会关闭其直接子视图控制器以及堆栈上该子视图控制器上方的所有视图控制器。
所以你不需要调用dismissViewControllerAnimated:completion:
三次,在你想回来的控制器上调用就足够了。此时,传递对该控制器的引用将比浏览视图控制器堆栈更可靠。
文档中有一些更有用的细节,例如关于一次关闭多个控制器时应用哪些转换。
我建议您阅读整个文档,不仅针对此方法,还针对您在应用程序中使用的所有方法/类。你很可能会发现能让你的生活更轻松的事情。
如果你没有时间阅读所有关于 UIKit 的 Apple 文档,你可以在遇到问题时阅读它,例如在这种情况下 dismissViewControllerAnimated:completion:
无法正常工作。
作为结束说明,您的方法存在一些更微妙的问题,因为实际解除发生在另一个 runloop 循环中,因为可能会生成控制台警告并且不会按预期运行。这就是为什么有关呈现/关闭其他控制器的进一步操作应在完成块中完成,以更改 UIKit
以完成其内部状态的更新。
【讨论】:
那么如果我只是关闭堆栈中的根视图控制器,那么所有其他视图控制器都会自动在堆栈中关闭? 是的,您可以同时从展示堆栈中弹出多个控制器。 您的“简短”答案有效。感谢您的信息。这是非常丰富和有趣的【参考方案2】:完全理解。我要做的是嵌入导航控制器而不是使用模式。我有一个和你一样的案例。我让LoginViewController
成为UINavigationController
的根视图控制器。 SignupViewController
将通过push
方法呈现。对于ResetPasswordViewController
,我将使用modal
,因为无论结果如何,它都应该回到LoginViewController
。然后,您可以从SignupViewController
或LoginViewController
中解散整个UINavigationController
。
第二种方法是,您想出自己的机制来通过共享实例引用呈现的UIViewController
。然后,您可以轻松地将其关闭。小心内存管理。关闭它后,您应该考虑是否需要立即将其 nil。
【讨论】:
【参考方案3】:我知道三种关闭多个视图控制器的方法:
使用完成块链~
UIViewController *theVC = self.presentingViewController;
UIViewController *theOtherVC = theVC.presentingViewController;
[self dismissViewControllerAnimated:NO
completion:^
[theVC dismissViewControllerAnimated:NO
completion:^
[theOtherVC dismissViewControllerAnimated:NO completion:nil];
];
];
使用 viewControllers 的 'viewWillAppear:' 方法
~
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
if (self.shouldDismiss)
CustomVC *theVC = (id)self.presentingViewController;
theVC.shouldDismiss = YES;
[self dismissViewControllerAnimated:NO completion:nil];
将 LoginVC1 的引用传递到更下游的链中。
(这是迄今为止最好的方法)
假设您有一些 StandardVC,它提供了 LoginVC1。
然后,LoginVC1 呈现 LoginVC2。
然后,LoginVC2 出现了 LoginVC3。
做你想做的事的一种简单方法是调用(从你的 LoginVC3.m 文件中)
[myLoginVC1dismissViewControllerAnimated:YES 完成:nil];
在这种情况下,您的 LoginVC1 将失去其强引用(来自 StandardVC),这意味着 LoginVC2 和 LoginVC3 也将被释放。
所以,你需要做的就是让你的 LoginVC3 知道 LoginVC1 的存在。
如果不想传递 LoginVC1 的引用,可以使用:
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:NO completion:nil];
但是,上述方法并不是做你想做的事情的正确方法。
我建议您执行以下操作:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
if (!self.isUserLoggedIn)
[UIApplication sharedApplication].keyWindow.rootViewController = self.myLoginVC;
return YES;
然后,当用户完成他的登录过程,你可以使用
[UIApplication sharedApplication].keyWindow.rootViewController = self.myUsualStartVC;
【讨论】:
以上是关于如何一次关闭 3 个模态视图控制器?的主要内容,如果未能解决你的问题,请参考以下文章