143

I need an instance of root view controller.

I tried those approaches:

UIViewController *rootViewController = (UIViewController*)[[[UIApplication sharedApplication] keyWindow] rootViewController];

Returns: null:

Also when I try to get an array of controllers:

NSArray *viewControllers = self.navigationController.viewControllers;

It returns only one controller, but it isn't my root view controller.

If I try to take from navigation controller:

UIViewController *root = (UIViewController*)[self.navigationController.viewControllers objectAtIndex:0];

Returns: null:

Any ideas why? What else could I try to get an instance of my root view controller?

Thanks.

Streetboy
  • 4,351
  • 12
  • 56
  • 101
  • The keyWindow is the active window,for example,when you show a UIAlertView,the UIAlertView's window is the keyWindow but it's not the AppDelegate's window.If you want to get application's rootViewController,maybe use [[[UIApplication sharedApplication] delegate] window] rootViewController ] is better. – 无夜之星辰 Jul 17 '17 at 03:28

9 Answers9

264

if you are trying to access the rootViewController you set in your appDelegate. try this:

Objective-C

YourViewController *rootController = (YourViewController*)[[(YourAppDelegate*)
                                   [[UIApplication sharedApplication]delegate] window] rootViewController];

Swift

let appDelegate  = UIApplication.sharedApplication().delegate as AppDelegate
let viewController = appDelegate.window!.rootViewController as YourViewController

Swift 3

let appDelegate  = UIApplication.shared.delegate as! AppDelegate
let viewController = appDelegate.window!.rootViewController as! YourViewController

Swift 4 & 4.2

let viewController = UIApplication.shared.keyWindow?.rootViewController as? YourViewController

Swift 5 & 5.1 & 5.2

let viewController = UIApplication.shared.windows.first?.rootViewController as? YourViewController
janusfidel
  • 8,036
  • 4
  • 30
  • 53
  • 4
    YourViewController *rootController =(YourViewController *)[[(YourAppDelegate *) [[UIApplication sharedApplication]delegate] window] rootViewController]; – Craig Moore Feb 17 '14 at 20:45
  • 1
    In Swift2, you have to use "let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate", to force unwrap – LiangWang Jun 18 '15 at 01:58
  • Is there any way to have two controllers assigned in this manner so that you can pull data from both to the watch? – Johnny Marin Mar 04 '16 at 18:10
  • 1
    This is a lifesaver. Thank you! Took me months to find this piece of delicious code! – ItsMeDom Feb 25 '19 at 15:43
  • AVPictureInPictureController or AVPlayerViewController will create new window which class is PGHostedWindow and add it to windows array, Therefore, using the first window may cause a crash – wlixcc Dec 11 '20 at 04:15
  • This doesn't seem to work in Catalyst. If I try this code from a VC that is presented by my root VC, I get the presented VC, not the root. Looking more closely, each one is in a separate window (I guess Catalyst is doing this). The one I'm really interested in is in UIApplication.shared.windows[1] (not first). – Victor Engel Nov 27 '21 at 21:06
48

Objective-C

UIViewController *controller = [UIApplication sharedApplication].keyWindow.rootViewController;

Swift 2.0

let viewController = UIApplication.sharedApplication().keyWindow?.rootViewController

Swift 5

let viewController = UIApplication.shared.keyWindow?.rootViewController
shim
  • 9,289
  • 12
  • 69
  • 108
Chamath Jeevan
  • 5,072
  • 1
  • 24
  • 27
  • 4
    This should be the top answer. +1 The other answers will not work if you need to reference the root view controller from a different framework that your main app is dependent on. – C. Tewalt Sep 06 '16 at 22:38
  • 7
    It's worth pointing out that keyWindow was deprecated in iOS13 – CristianMoisei Oct 17 '20 at 01:16
11

As suggested here by @0x7fffffff, if you have UINavigationController it can be easier to do:

YourViewController *rootController =
    (YourViewController *)
        [self.navigationController.viewControllers objectAtIndex: 0];

The code in the answer above returns UINavigation controller (if you have it) and if this is what you need, you can use self.navigationController.

Community
  • 1
  • 1
esp
  • 7,314
  • 6
  • 49
  • 79
10

For Swift 5 and later, I recommend use this method:

UIApplication.shared.windows.last?.rootViewController

Use .last will return top view Controller is active.

.first sometimes doesn't work with libraries that require a window on top & actived.

For example: Admob . I have the ad not showing and find that I am calling the hidden window underneath when using sheet or fullscreenCover.

NOTE: View want to display ads required in NavigationView, if not, ads will be not run.

Example: NavigationView { View1() }

I hope it is useful.

Thiên Quang
  • 368
  • 3
  • 9
6

iOS 15

With Scenes and some APIs being deprecated (like UIApplication.shared.keyWindow), the most universal approach I've found is this:

let scene = UIApplication.shared.connectedScenes
    .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene

let rootViewController = scene?
    .windows.first(where: { $0.isKeyWindow })?
    .rootViewController

Note: If your app truly supports multiple windows/scenes, that means you have multiple "root View Controllers" and this code is not for you :)

Dima G
  • 1,945
  • 18
  • 22
4

Unless you have a good reason, in your root controller do this:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(onTheEvent:)
                                             name:@"ABCMyEvent"
                                           object:nil];

And when you want to notify it:

[[NSNotificationCenter defaultCenter] postNotificationName:@"ABCMyEvent"
                                                object:self];                
wisty
  • 6,981
  • 1
  • 30
  • 29
2

Swift 3

let rootViewController = UIApplication.shared.keyWindow?.rootViewController
shim
  • 9,289
  • 12
  • 69
  • 108
cybermach
  • 125
  • 1
  • 6
1

Swift way to do it, you can call this from anywhere, it returns optional so watch out about that:

/// EZSwiftExtensions - Gives you the VC on top so you can easily push your popups
var topMostVC: UIViewController? {
    var presentedVC = UIApplication.sharedApplication().keyWindow?.rootViewController
    while let pVC = presentedVC?.presentedViewController {
        presentedVC = pVC
    }

    if presentedVC == nil {
        print("EZSwiftExtensions Error: You don't have any views set. You may be calling them in viewDidLoad. Try viewDidAppear instead.")
    }
    return presentedVC
}

Its included as a standard function in:

https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
1

Swift 3: Chage ViewController withOut Segue and send AnyObject Use: Identity MainPageViewController on target ViewController

let mainPage = self.storyboard?.instantiateViewController(withIdentifier: "MainPageViewController") as! MainPageViewController

var mainPageNav = UINavigationController(rootViewController: mainPage)

self.present(mainPageNav, animated: true, completion: nil)

or if you want to Change View Controller and send Data

let mainPage = self.storyboard?.instantiateViewController(withIdentifier: "MainPageViewController") as! MainPageViewController

let dataToSend = "**Any String**" or  var ObjectToSend:**AnyObject** 

mainPage.getData = dataToSend 

var mainPageNav = UINavigationController(rootViewController: mainPage)

self.present(mainPageNav, animated: true, completion: nil)  
SimpleJ
  • 13,812
  • 13
  • 53
  • 93