0
votes

I have inherited a navigation controller issue in an existing app that I'm trying to solve cleanly.

This app has multiple storyboards and multiple UINavigationControllers. At one point in the app, a series of view controllers is presented modally, using a separate storyboard and a separate nav controller. When the modal process is complete, the navigation hierarchy looks something like this:

NavController1 -> VC1 ['Present Modally' segue] -> NavController2 -> VC2 -> VC3 -> VC4

When the user completes the modal activity in VC4, dismiss() is called programmatically on VC4 and the user can then navigate back to VC1 using the back button.

However, what we really need to do is to 'pop off' all of the modally presented set of view controllers (and their nav controller) when the user finishes the modal activity. The problem is that from VC3 or VC4 I can't call popToRootViewController(). I also can't traverse down the VC stack to find VC1, since the current Nav controller doesn't manage it.

A couple solutions come to mind:

1) use the notification manager and have VC1 listen for the message to pop everything off back to itself

2) pass a reference to VC1 as a delegate all the way up the chain so that VC3 or 4 can have it pop everything off

Both of these solutions follow the general maxim that the presenting VC should be the one that dismisses, but neither are what I would consider clean.

I would welcome any thoughts or alternative solutions.

2

2 Answers

0
votes

Assuming that these are the way the view controllers were laid out:

NavController1 --['Root View Controller' segue]--> VC1 --['Present Modally' segue]--

--> NavController2 --['Root View Controller' segue]--> VC2 --['Push' segue]--> VC3 --['Push' segue]--> VC4

You should be able to go back to VC1 by dismissing either VC2, VC3 or VC4.

// example for vc4
vc4.navigationController?.dismiss(animated: true, completion: nil)

However, if each of the viewControllers were presented modally, you should be able to traverse through the presentingViewController to reach VC1.

var currentVC: UIViewController? = self
var presentingVC: UIViewController? = currentVC?.presentingViewController
while presentingVC != nil && !(presentingVC is VC1) {
    currentVC?.dismiss(animated: true, completion: nil)
    currentVC = presentingVC
    presentingVC = currentVC?.presentingViewController
}

Hope that helps.

0
votes

When popping, you may kick out the viewControllers from your navigation Controller, would solve your problem

extension UINavigationController { 
     public func removeViewController(classes : [String]) {
           var vcs = [UIViewControllers]()
           for viewController in self.viewControllers {
               let name = viewController.className
               if !classes.contains(name) {
                    vcs.append(viewController)
                }
           }
           if classes.count < vcs.count {
               self.viewControllers = vcs
           }
    }
}

now think you have 4 viewControllers , A, B, C, D, you want to remove B and C and Move Back To A

In D's View Controller

override func viewDidLoad() {
   super.viewDidLoad()
   //your works
   let viewControllersToRemove = [String(describing: type(of:B)), String(describing: type(of:C))]
   navigationController.removeViewControoler(classes : viewControllersToRemove)
}