简体   繁体   中英

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

My app is contained in a UINavigationController . The root view controller A is essentially a login view, where the user will enter credentials and upon success will present the profile view controller B via a push segue. Right now the user gets the back button on the nav bar of B to navigate back to A . I want B to now be the root view controller on the navigation stack, effectively preventing the user from ever getting back to A until a new app launch.

What is the correct way to handle this while still using a push segue? Should I perform the presentation of B without a segue and effectively start B off with a new navigation controller?

Just replace the rootViewController of your UIWindow . controllerA and controllerB can be any Viewcontroller -Class you want.

You could use a simple UITableViewController for the Loginpage and then replace the rootViewController of your UIWindow with a UINavigationController holding controllerB

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

I know the question is slightly old, but having gone through this myself, I have finally found a solution which I think is better than the accepted answer.

  1. Make the login controller the root controller of the navigation controller.

  2. When login succeeds, remove the login controller from the navigation controllers viewcontrollers array and append your app's initial 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. The only other thing you have to do is add the following line to your initial viewcontroller's viewDidLoad:

     navigationItem.hidesBackButton = true 

The setViewControllers method with animated set to true takes care of the push animation you specified as a requirement.

EDIT:

I have made a method that can be added to the login controller, which not only works if you want the login controller to be shown as the first view controller in the app, but also if later authentication fails (expired credentials etc.) and you have shown the login controller modally:

    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)
    }

Just remember step 3 from above.

B should be contained in it's own navigation controller since A will no longer be accessible. You'll have to use something other than a push segue. transitionWithView works well:

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

I was using segue to navigate from a controller A with navigation to another controller B , And i wanted to get controller A deinitialized after the pushing to controller B so, I would say this was the cleanest and easiest way to remove controller A in viewDidDisappear and get it deinitialized.

 final class A: UIViewController {

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM