How do I get the RootViewController from a pushed controller?

Viewed 146143

So, I push a view controller from RootViewController like:

[self.navigationController pushViewController:anotherViewController animated:YES] ;

BUT, FROM anotherViewController now, I want to access the RootViewController again.

I'm trying

// (inside anotherViewController now)
///RootViewController *root = (RootViewController*)self.parentViewController ; // No.
// err
RootViewController *root = (RootViewController*)[self.navigationController.viewControllers objectAtIndex:0] ; // YES!! it works

I'm not sure WHY this works and I'm not sure if its the best way to do it. Can somebody comment on a better way to get the RootViewController from a controller you've pushed into that RootViewController's navigationController and whether or not the way I've done it is reliable or not?

9 Answers

I encounter a strange condition.

self.viewControllers.first is not root viewController always.

Generally, self.viewControllers.first is root viewController indeed. But sometimes it's not.

class MyCustomMainNavigationController: UINavigationController {

    function configureForView(_ v: UIViewController, animated: Bool) {
        let root = self.viewControllers.first
        let isRoot = (v == root)
    
        // Update UI based on isRoot
        // ....
    }
}

extension MyCustomMainNavigationController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, 
        willShow viewController: UIViewController, 
        animated: Bool) {

        self.configureForView(viewController, animated: animated)
    }
}

My issue:

Generally, self.viewControllers.first is root viewController. But, when I call popToRootViewController(animated:), and then it triggers navigationController(_:willShow:animated:). At this moment, self.viewControllers.first is NOT root viewController, it's the last viewController which will disappear.

Summary

  • self.viewControllers.first is not always root viewController. Sometime, it will be the last viewController.

So, I suggest to keep rootViewController by property when self.viewControllers have ONLY one viewController. I get root viewController in viewDidLoad() of custom UINavigationController.

class MyCustomMainNavigationController: UINavigationController {
    fileprivate var myRoot: UIViewController!
    override func viewDidLoad() {

        super.viewDidLoad()

        // My UINavigationController is defined in storyboard. 
        // So at this moment, 
        // I can get root viewController by `self.topViewController!`
        let v = self.topViewController!
        self.myRoot = v
    }

}

Enviroments:

  • iPhone 7 with iOS 14.0.1
  • Xcode 12.0.1 (12A7300)

This worked for me:

When my root view controller is embedded in a navigation controller:

UINavigationController * navigationController = (UINavigationController *)[[[[UIApplication sharedApplication] windows] firstObject] rootViewController];
RootViewController * rootVC = (RootViewController *)[[navigationController viewControllers] firstObject];

Remember that keyWindow is deprecated.

Related