Search code examples
swiftuinavigationcontrollerpopviewcontrolleranimated

How to do popViewController from another class?


My app has a TabBarController. Each tabBarItem relates to a ViewController embedded in a NavigationController.

When in first tabBarItem and selecting another tabBarItem I want to do some stuff before moving to the selected ViewController. Therefore I created a class for my tabBarController and made it UITabBarControllerDelegate.

What I want to do is present an alert with two buttons; button A cancels the move to the selected viewController and button B lets the move happen.

My problem is that when button B is pressed, I want to popToRootViewController. I gave the navigationController a storyboardID and tried to instantiate it like shown below.

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
    
    if let alert = self.storyboard?.instantiateViewController(withIdentifier: "ActiveSessionWarningAlert") as? ActiveSessionWarningAlert {
            
        alert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
        alert.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
            
        let alertWasDismissed: (Bool) -> Void = { userWantsToMoveToSelectedViewController in
                
            if userWantsToMoveToSelectedViewController {
                    
                if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController {
                
                    navContr.popToRootViewController(animated: true)

                }
                    
                tabBarController.selectedViewController = viewController
                    
            
            }
        }
        
        alert.alertWasDismissed = alertWasDismissed
        self.present(alert, animated: true, completion: nil)
        
    }

    return false

}

Everything works as expected, but the popToRootViewController doesn't seem to occur; when selecting the first tabBarItem again the same viewController that was 'active' when we left the item is still showing.

I checked so that the viewController I want to pop is actually in the navigation stack and that the navContr != nil.

What am I missing?


Solution

  • You don't say so, but I'm assuming that the alertWasDismissed closure you pass to your alert view controller gets invoked when the user dismisses the alert.

    The problem with your closure is this bit:

    if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController 
    

    Any time you call instantiateViewController(withIdentifier:), you are creating a brand new, never before seen instance of a view controller (a navigation controller in this case.) That navigation controller has nothing to do with the one that belongs to the current tab that you are trying to dismiss. It doesn't have anything in it's navigation stack other than the root view controller that is defined in the storyboard.

    What you need to do is find the navigation controller of the current tab in your tab bar controller's tabBarController(_:shouldSelect:) method, and pass that navigation controller to your alertWasDismissed closure.

    At the time the tabBarController(_:shouldSelect:) method is called, the tab bar controller's selectedViewController should contain the current view controller. Replace the line above with:

    if let navContr = tabBarController.selectedViewController? as? UINavigationController {}
    

    That should work.