我有一个具有初始登录屏幕的应用程序,然后当用户想要注册时,他们会看到一个注册表单,它是三个以模态方式呈现的视图控制器。当用户在第三个屏幕上完成表格时(通过按“完成”按钮),我希望用户被带回初始登录屏幕。
我试过在第三个视图控制器中执行此操作:
[self dismissViewControllerAnimated:NO completion:nil]
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil]
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:NO completion:nil]
然而,它只解散了两个视图控制器而不是全部3.为什么会发生这种情况?
答案 0 :(得分:2)
正如其他人所指出的那样,从UX的角度来看,有更优雅/高效/更简单的方法来实现类似的结果:通过导航控制器,或页面视图控制器,或其他容器。
简短/快速回答:你需要在呈现视图控制器的链条中更进一步,因为解雇请求需要发送到呈现的控制器,而不是发送到那个“控制器”的控制器。正在呈现。并且您只能将解除请求发送到该控制器,它将负责从子堆栈中弹出子控制器。
UIViewController *ctrl = self.presentingViewController.presentingViewController.presentingViewController;
[ctrl dismissViewControllerAnimated:NO completion:nil]
要解释原因,并希望帮助其他人更好地理解iOS中的控制器呈现逻辑,您可以在下面找到更多详细信息。
让我们从dismissViewControllerAnimated:completion:
上的Apple documentation
取消视图控制器以模态方式显示的视图控制器。
呈现视图控制器负责解除它所呈现的视图控制器。如果您在呈现的视图控制器本身上调用此方法,UIKit会要求呈现视图控制器处理解雇。
因此[self dismissViewControllerAnimated:NO completion:nil]
只是将请求转发给self.presentingViewController
。这意味着前两行具有相同的效果(实际上第二行没有做任何事情,因为在第一行执行后没有出现控制器)。
这就是为什么你对视图控制器的解雇仅适用于前两个视图控制器。您应该从self.presentingViewController
开始,然后沿着呈现视图控制器的链条前进。但是这不是很优雅,如果稍后在视图控制器的层次结构上发生变化,则会导致问题。
继续阅读文档,我们偶然发现了这一点:
如果连续呈现多个视图控制器,从而构建一堆呈现的视图控制器,则在堆栈中较低的视图控制器上调用此方法会解除其直接子视图控制器以及堆栈上该子视图上方的所有视图控制器。 / p>
所以你不需要三次拨打dismissViewControllerAnimated:completion:
,你想要回来的控制器上的呼叫就足够了。此时,将引用传递给该控制器比在视图控制器堆栈中导航更可靠。
文档中有一些更有用的细节,例如关于一次解除多个控制器时应用的转换。
我建议您浏览整个文档,不仅是针对此方法,还针对您在应用程序中使用的所有方法/类。您可能会发现能让您的生活更轻松的事情。
如果您没有时间阅读关于UIKit的所有Apple文档,您可以在遇到问题时阅读它,例如在这种情况下dismissViewControllerAnimated:completion:
无法正常工作以为它会。
作为结束语,您的方法存在一些更微妙的问题,因为实际的解雇发生在另一个runloop循环中,因为它可能会生成控制台警告而不是按预期运行。这就是为什么应该在完成块中进行有关呈现/解除其他控制器的进一步操作,以更改UIKit
以完成更新其内部状态。
答案 1 :(得分:0)
我知道有三种方法可以解雇几个viewControllers:
〜
UIViewController *theVC = self.presentingViewController;
UIViewController *theOtherVC = theVC.presentingViewController;
[self dismissViewControllerAnimated:NO
completion:^
{
[theVC dismissViewControllerAnimated:NO
completion:^
{
[theOtherVC dismissViewControllerAnimated:NO completion:nil];
}];
}];
〜
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.shouldDismiss)
{
CustomVC *theVC = (id)self.presentingViewController;
theVC.shouldDismiss = YES;
[self dismissViewControllerAnimated:NO completion:nil];
}
}
(这是迄今为止最好的方法)
想象一下,你有一些StandardVC,它提供了LoginVC1。
然后,LoginVC1呈现了LoginVC2。
然后,LoginVC2呈现了LoginVC3。
执行您想要的操作的简单方法是调用(从您的LoginVC3.m文件中)
[myLoginVC1 dismissViewControllerAnimated:YES completion: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;
答案 2 :(得分:0)
完全理解。我要做的是嵌入导航控制器而不是使用模态。我有一个像你一样的案子。我LoginViewController
是UINavigationController
的根视图控制器。 SignupViewController
将以push
方式呈现ResetPasswordViewController
。对于modal
,我将使用LoginViewController
,因为无论结果如何,都应该返回UINavigationController
。然后,您可以从SignupViewController
或LoginViewController
中删除整个UIViewController
。
第二种方法就是,你想出了自己的机制来通过共享实例引用所呈现的 var videoArray = [["videoName":"Nameofvideo", "inputtype": "mp4"], ["videoName":"Nameofvideo2", "inputtype": "mp4"]]
。然后,你可以很容易地解雇它。小心内存管理。在解雇之后,你应该考虑是否需要立即取消它。