推送 segue 后从导航堆栈中删除视图控制器(使用 Storyboard segues)

Remove view controller from navigation stack after push segue (using Storyboard segues)

我的应用包含在 UINavigationController 中。根视图控制器 A 本质上是一个登录视图,用户将在其中输入凭据,成功后将通过推送显示配置文件视图控制器 B继续。现在,用户在 B 的导航栏上获得后退按钮以导航回 A。我希望 B 现在成为导航堆栈上的根视图控制器,有效地防止用户在新应用程序启动之前返回到 A .

在仍然使用推送 segue 的情况下处理此问题的正确方法是什么?我是否应该在没有 segue 的情况下执行 B 的演示,并使用新的导航控制器有效地启动 B

B 应该包含在它自己的导航控制器中,因为 A 将不再可用。您将不得不使用 push segue 以外的东西。 transitionWithView 效果不错:

UINavigationController *newNavController;

[UIView transitionWithView:delegate.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^
 {
     [delegate.window addSubview:newNavController.view];
 }
                completion:^(BOOL finished)
 {
     delegate.window.rootViewController = newNavController;
 }];

[delegate.window makeKeyAndVisible];

只需替换 UIWindowrootViewControllercontrollerAcontrollerB 可以是您想要的任何 Viewcontroller-Class。

您可以为 Loginpage 使用一个简单的 UITableViewController,然后替换 UIWindowrootViewController UINavigationController 持有 controllerB

[UIView transitionFromView:controllerA.view
                        toView:controllerB.view
                      duration:0.65f
                       options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve)
                    completion:^(BOOL finished){
                        delegate.window.rootViewController = controllerB;
                    }];

我知道这个问题有点老了,但是我自己经历了这个,我终于找到了一个我认为比接受的答案更好的解决方案。

  1. 将登录控制器设为导航控制器的根控制器。

  2. 登录成功后,从导航控制器 viewcontrollers 数组中删除登录控制器并附加应用程序的初始 viewcontroller。

    if let navigationController = navigationController {
    
        var viewControllers = navigationController.viewControllers
    
        for (index, viewController) in viewControllers.enumerated() where viewController is LoginViewController {
            viewControllers.remove(at: index)
        }
    
        let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let initialViewController = storyBoard.instantiateViewController(withIdentifier: "InitialViewController")
    
        viewControllers.append(initialViewController)
    
        navigationController.setViewControllers(viewControllers, animated: true)
    }
    
  3. 您唯一需要做的另一件事是将以下行添加到初始 viewcontroller 的 viewDidLoad:

    navigationItem.hidesBackButton = true
    

将 animated 设置为 true 的 setViewControllers 方法会处理您指定为要求的推送动画。

编辑:

我已经制作了一个可以添加到登录控制器的方法,它不仅适用于您希望登录控制器显示为应用程序中的第一个视图控制器,而且适用于以后的身份验证失败(凭据过期)等)并且您已经以模态方式显示了登录控制器:

    func authenticationDidSucceed() {
        // If the LoginController is in a navigation stack then
        // replace it with the initial viewcontroller of the app
        guard navigationController?.viewControllers.contains(self) ?? false else {
            // Else it must be presented modally, so dismiss it
            dismiss(animated: true, completion: nil)
            return
        }

        guard let navigationController = navigationController else { return }

        var viewControllers = navigationController.viewControllers

        if let index = viewControllers.index(of: self) {
            viewControllers.remove(at: index)
        }

        viewControllers.append(UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "InitialViewController"))

        navigationController.setViewControllers(viewControllers, animated: true)
    }

记住上面的第 3 步。

我正在使用 segue 从一个 controller A 导航到另一个 controller B,我想在推送到 controller B 之后取消初始化 controller A 所以,我会说这是删除 viewDidDisappear 中的 controller A 并将其取消初始化的最干净、最简单的方法。

 final class A: UIViewController {

    override func viewDidDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.viewControllers.removeAll(where: { self === [=10=] })
    }
}