如何在任何时候(甚至在转场期间)或在安全地解除视图控制器时解除视图控制器?

5
我有一个连接到服务器的iOS应用程序。如果我们断开连接,我想能够关闭顶层视图控制器以返回到“连接到服务器”视图控制器。问题是,断开连接可能发生在任何时间,包括在视图控制器之间的转换期间。
视图控制器层次结构如下:
1. 连接到服务器视图控制器(ConnectingToServerViewController) 2. 登录视图控制器(SignInViewController) 3. 主要应用程序视图控制器(MainAppViewController) 4. 其他视图控制器
当检测到断开连接时,我希望视图层次结构会折叠回去,变为:
1. 连接到服务器视图控制器(ConnectingToServerViewController)
因此,在检测到连接中断时,将调用此方法来关闭ConnectingToServerViewController已经呈现的任何内容,并返回尝试连接到服务器:
```swift func resetViewHierarchy() { dismiss(animated: true, completion: nil) navigationController?.popViewController(animated: true) } ```
- (void)restartSession
{
    if (self.presentedViewController) {
        [self dismissViewControllerAnimated:NO completion:nil];
    }
}

然而,如果我在视图转换过程中尝试关闭应用程序,则会出现错误,例如:
*** Assertion failure in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:], /SourceCache/UIKit/UIKit-2380.17/UIWindowController.m:211

attempt to dismiss modal view controller whose view does not currently appear. self = <YYYYYViewController: 0x2089c8a0> modalViewController = <XXXXXViewController: 0x208e6610>
attempt to dismiss modal view controller whose view does not currently appear. self = <WWWWWWViewController: 0x1fd9e990> modalViewController = <YYYYYViewController: 0x2089c8a0>

第一种方式会导致应用程序崩溃,第二种方式则不会解除任何内容并继续显示当前呈现的视图控制器。

想法:

  1. 由于我们不知道何时开始延迟,所以延迟不起作用。
  2. 有没有一种方法可以跟踪视图转换何时完成?
  3. 是否应该让所有视图控制器覆盖willAppear、didAppear并在安全解除时警告应用程序?
  4. 也许,我应该设置一个新的根视图控制器,而不是解除?
  5. 我确保所有重写的view(will|did)(dis)?appear方法都调用了适当的super方法。
  6. 任何需要所有视图控制器覆盖view(did|will)appear方法来跟踪状态的解决方案听起来都可能会引起问题,如果我们忘记为新视图控制器设置基类。

如果您可以在viewController中设置任何标志变量,并且当与服务器断开连接时,如果视图未加载,则可以从restrtSesssion方法更改标志。这样,当视图加载时,它将自动关闭。您可以使用类似的逻辑...希望它有所帮助.. - Armaan Stranger
我在最后稍微提到了这个问题。我想避免那些需要所有视图控制器都必须子类化一个特殊的视图控制器类来跟踪状态的解决方案。 - Nick Sonneveld
如果想要延迟或在主队列上解雇它,可以将解雇操作放在 NSOperationQueue.mainQueue().addOperationWithBlock { // dismiss } 中。// Swift 代码 - Erik Engheim
4个回答

0

看起来您正试图在视图控制器当前未显示在屏幕上时将其取消。为了检查它是否在屏幕上,您可以使用:

if (self.presentedViewController.view.window) 
{
    [self dismissViewControllerAnimated:NO completion:nil];
}
else 
{
    self.presentedViewController = nil;
}

但是当呈现的视图控制器最终可见时,我仍然需要解除吗? - Nick Sonneveld
如果它不在屏幕上,您可以在viewController中设置一个标志来指示模态视图需要关闭。然后在您的viewWillAppear:方法中,您可以检查该标志并在必要时将其关闭。我不确定这个方法是否可行,因为我从未尝试过,但这只是一个想法。 - Jamie
嗯,我们可能不得不这样做,但我想避免让每个视图控制器都检查是否需要关闭自己。感觉我们可能会忘记为新的视图控制器做这件事。而且我们有很多视图控制器。 - Nick Sonneveld
你尝试过将模态视图控制器设置为nil吗?如果它不在屏幕上,这样做可能会有帮助。 - Jamie

0

做类似这样的事情。试一试这个。

UIViewController *controller = self.presentingViewController; //THIS LINE IS IMP
[self dismissViewControllerAnimated:YES
                                 completion:^{
                                     [controller presentViewController:adminViewController animated:YES completion:nil];
                                     adminViewController.view.superview.frame    = CGRectMake(1024/2 - 400, 768/2 - 280, 800 , 560);//it's important to do this after
                                     [adminViewController release];
                                 }]; 

断言可能会在完成块被调用之前发生。 - Nick Sonneveld
当调用dismissViewControllerAnimated:completion时,会出现以下断言: 在-[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]中,/SourceCache/UIKit/UIKit-2380.17/UIWindowController.m:211发生了断言错误。 这个错误会在完成块被调用之前出现。 - Nick Sonneveld
如果您即将进行下一个 VC 的演示,请确保在 ViewDidAppear 中检查一次您的代码。 - βhargavḯ
每个视图控制器都应该在viewDidAppear中进行检查,这听起来不太对。 - Nick Sonneveld

0
一个对我有效的方法是将新的视图控制器分配给根视图控制器。这样,旧层次结构中的视图可以自由地进行动画和转换,而我们则拥有了新的控制器。
例如:
- (void)restartSession
{
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    ConnectingToServerViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"ConnectingToServerViewController"];
    vc.modalPresentationStyle = UIModalPresentationFullScreen;
    [UIApplication sharedApplication].delegate.window.rootViewController = vc;
}

我不确定我是否意识到了所有这些缺点。也许旧的视图控制器永远不会被释放,因为存在悬空的强引用?我们不再重用ConnectingToServerViewController,每次都必须重新创建它。

我基于iOS中管理和解除多个视图控制器的答案中所看到的代码。


0

我会按顺序回答。

有没有一种方法可以跟踪视图转换何时完成?

您可以尝试使用 UINavigationControllerDelegate(如果您正在使用其中之一)。另一种方法是使用自定义动画器。

所有视图控制器都应该覆盖willAppear,didAppear并在安全关闭应用程序时发出警报吗?

这是一个选项。如果您想要,可以自由选择这样做。另一个选择是不这样做。我认为像导航控制器这样的容器视图控制器有更好的方法。

我应该只设置一个新的根视图控制器吗?

我建议相反。我会将 SignInViewController / MainAppViewController 设置为根流,并根据需要以模态方式呈现 ConnectingToServerViewController。在我看来,这是一种更健康的方法。

希望能有所帮助。


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