TIMEX TIMEX - 24 days ago 10
Swift Question

How to correctly dismiss a UINavigationController that's presented as a modal?

In my

TabBarViewController
, I create a UINavigationController and present it as a modal.

var navController = UINavigationController()
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
self.presentViewController(self.navController, animated: false, completion: nil)
self.navController.pushViewController(messageVC, animated: false)


Inside my
MessageViewController
, this is how I want to dismiss it:

func swipedRightAndUserWantsToDismiss(){
if self == self.navigationController?.viewControllers[0] {
self.dismissViewControllerAnimated(true, completion: nil) //doesn't deinit
}else{
self.navigationController?.popViewControllerAnimated(true) //deinits correctly
}
}

deinit{
print("Deinit MessagesViewController")
}


The problem is that when I get to the root View Controller and try to dismiss both the child and the UINavigationController, my
MessagesViewController
deinit does not get called. Something's holding on to it -- most likely UINavigationController

Answer

Your controller hierarchy looks like this:

UITabViewController
    |
    | presents
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

Now, if you are inside MessagesViewController, then its navigationController is the one that is being presented and that's the one you should be dismissing but calling dismiss on MessagesViewController should work too.

However, the problem is that dismissing the navigation controller won't remove its view controllers. It seems you are holding to your navigation controller (since you are presenting it using self.navController) so the state will become

UITabViewController
    |
    | self.navController holds a reference to
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

To properly destroy MessagesViewController you will have to either let go of the navController or you will have to pop to root (thus removing MessagesViewController from view hierarchy).

The typical solution would be not to save a reference to navController at all. You could always create a new UINavigationController when presenting. Another solution is using a delegate - instead of dismissing from inside MessagesViewController, let it call back to the presenter, which would call

self.navController.dismissViewControllerAnimated(true, completion: {
   self.navController = nil;
});