Search code examples
iosswiftuinavigationcontrollerdelegatessingleton

Are specific instances of a view controller accessible without the use of singletons?


When a UINavigationController pushes a view controller into view, the nav controller creates its own instance of that view controller behind the scenes. Using this nav controller delegate:

extension Main_ProfileViewController: UINavigationControllerDelegate {

    // called just after the navigation controller displays a view controller’s view and navigation item properties
    open func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        print(viewController)
    }

}


That instance can be "determined". The console prints something like this:

<ElChapo.SomeRandomViewController: 0x7fbe8b003640>

That 14-character hash is different each time that view controller is pushed by the nav controller so clearly they are different instances each time. My question: is that specific instance somehow accessible without using singletons? By accessible, I mean accessible where I can run a delegate to it and call specific methods within that specific instance.

This is what I'm trying to accomplish:

To pop a navigation stack back to its root, I would execute this function from within the view controller at the top of the stack.

// pop to root
func popToRoot() {
    self.navigationController?.popToRootViewController(animated: true)
}

This method must be called from within that specific instance of that view controller. If I call popToRoot() from another object, like the tab bar, by passing a delegate to it, it doesn't work because the delegate needs to find that exact instance to execute the pop.


Solution

  • The Nav controller doesn't "create its own instance", or at least that is a somewhat confused way of looking at it. It only appears that way if you are using storyboards. You can use a Navigation Controller without use of storyboards at all if you so wish, in which case you would manually instantiate a new View Controller to push onto the nav controller stack. If you use a storyboard, it will contain defined segue's which "cause" the next view controller to get pushed onto the stack. It is the segues in the storyboard that define which view controllers get created when. There is no need to create a singleton to "get to" the child view controllers pushed onto the navigation stack. Each time one is pushed onto the navigation controller's stack, by default it will be created, and when it is popped, unless you specifically maintain a strong reference to it (which would almost always be a bad idea) it will be destroyed.

    If you haven't already subclassed your navigation view controller, do so, and in the subclass override the prepare(for:sender:) to be able from the navigation controller, to access an instance of the child view controller pushed onto the navigation controller's stack. If you want to access that view controller, either save a reference to it in that method, or, better, find it in the navigation controllers stack using one of the following properties of the navigation controller:

    var topViewController: UIViewController?
    var visibleViewController: UIViewController?
    var viewControllers: [UIViewController]
    

    BTW the reference number you refer to isn't a hash but rather the hexadecimal value of a pointer (e.g. to the memory address) of where the view controller object is stored, but as you say, distinct view controllers will have distinct hex values.