60

Is there a way to get the current view controller from the AppDelegate? I know there is rootViewController, but that's not what I'm looking for.

aoakenfo
  • 914
  • 1
  • 7
  • 9

11 Answers11

65

If your app's root view controller is a UINavigationController you can do this:

((UINavigationController*)appDelegate.window.rootViewController).visibleViewController;

Similarly, if it's a UITabBarController you can do this:

((UITabBarController*)appDelegate.window.rootViewController).selectedViewController;

Of course, explicit casting like this is dirty. Better would be to capture the reference yourself using strong types.

devios1
  • 36,899
  • 45
  • 162
  • 260
32

This might help

- (UIViewController *)topViewController{
  return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
  if (rootViewController.presentedViewController == nil) {
    return rootViewController;
  }

  if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
    UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
    UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
    return [self topViewController:lastViewController];
  }

  UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
  return [self topViewController:presentedViewController];
}

Swift version:

extension UIApplication {
    class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return topViewController(base: nav.visibleViewController)
        }
        if let tab = base as? UITabBarController {
            if let selected = tab.selectedViewController {
                return topViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topViewController(base: presented)
        }
        return base
    }
}

Taken from: https://gist.github.com/snikch/3661188

user1898712
  • 368
  • 6
  • 18
Daniel Ryan
  • 6,976
  • 5
  • 45
  • 62
25

If you have UINavigationController into appDelegate then use its property topViewController or visibleViewController

beryllium
  • 29,669
  • 15
  • 106
  • 125
16

Make an extension:

extension UIApplication {
    class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return topViewController(nav.visibleViewController)
        }
        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController where top.view.window != nil {
                return topViewController(top)
            } else if let selected = tab.selectedViewController {
                return topViewController(selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topViewController(presented)
        }
        return base
    }
}

Usage:

if let rootViewController = UIApplication.topViewController() {
    //do sth with root view controller
}
Melvin
  • 3,421
  • 2
  • 37
  • 41
Alvin George
  • 14,148
  • 92
  • 64
  • This answer fixed an annoying problem with Apple's Advanced NSOperations sample classes. If an error occurred the topmost view controller is the only one that can present an alert. I might have made it iterative instead of recursive, because I'm old fashioned, but this is much better than blindly using UIApplication.sharedApplication().keyWindow?.rootViewController – Bob Wakefield Sep 02 '16 at 02:49
12

Get the appDelegate object:

MyAppDelegate *tmpDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];

As beryllium suggested you can use the UINavigationController's properties to access your current view controller.

So the code would look like:

id myCurrentController = tmpDelegate.myNavigationController.topViewController;

or:

NSArray *myCurrentViewControllers = tmpDelegate.myNavigationController.viewControllers;
pasawaya
  • 11,515
  • 7
  • 53
  • 92
Quasar
  • 137
  • 3
9

You can get the current view controller from rootViewController by looking for its presentedViewController, like this:

UIViewController *parentViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;

while (parentViewController.presentedViewController != nil){
    parentViewController = parentViewController.presentedViewController;
}
UIViewController *currentViewController = parentViewController;

It works with me. Hope it helps :)

MattZ
  • 381
  • 3
  • 7
5

For anyone not using a UINavigationControllerbut rather their default view controller is a UIViewController you can check which view controller is active (or presented) with the following in AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if let rootViewController = self.window!.rootViewController {
        if let presentedViewController = rootViewController.presentedViewController {
            return presentedViewController.supportedInterfaceOrientations()
        }
    } // Else current view controller is DefaultViewController

    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

As you can see I'm checking for the current view controller in order to support different interface orientations for specific view controllers. For anyone else interested in using this method to support specific the following should be placed in each view controller that needs a specific orientation.

override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.All.rawValue)
} 

Note: This code was written with Swift 1.2

Clay Ellis
  • 4,960
  • 2
  • 37
  • 45
5

UIApplication extension in Swift 4+ syntax based on A.G's solution

public extension UIApplication {

    class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return topViewController(base: nav.visibleViewController)
        }
        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController, top.view.window != nil {
                return topViewController(base: top)
            } else if let selected = tab.selectedViewController {
                return topViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topViewController(base: presented)
        }
        return base
    }
}

Sample usage:

if let rootViewController = UIApplication.topViewController() {
     //do something with rootViewController
}
Maverick
  • 3,209
  • 1
  • 34
  • 40
4

Swift Solution:

 self.window.rootViewController.presentedViewController. 

That should get you what you need.

Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
0

Often I need to retrieve the view controller that is currently displayed. It could mean the view controller at the top of the stack of the current UINavigationController, the currently presented view controller, etc. So I wrote this function which figures it out most of the time, and that you can use inside a UIViewController extension.

Code in Swift 3:

func currentViewController(
_ viewController: UIViewController? =
    UIApplication.shared.keyWindow?.rootViewController)
        -> UIViewController? {
    guard let viewController =
    viewController else { return nil }

    if let viewController =
        viewController as? UINavigationController {
        if let viewController =
            viewController.visibleViewController {
            return currentViewController(viewController)
        } else {
            return currentViewController(
                viewController.topViewController)
        }
    } else if let viewController =
            viewController as? UITabBarController {
        if let viewControllers =
            viewController.viewControllers,
            viewControllers.count > 5,
            viewController.selectedIndex >= 4 {
            return currentViewController(
                viewController.moreNavigationController)
        } else {
            return currentViewController(
                viewController.selectedViewController)
        }
    } else if let viewController =
            viewController.presentedViewController {
        return viewController
    } else if viewController.childViewControllers.count > 0 {
        return viewController.childViewControllers[0]
    } else {
        return viewController
    }
}

Call it with: currentViewController()

Paolo Musolino
  • 614
  • 7
  • 9
0

If anyone wants in Objective C.

GlobalManager.h

//
//  GlobalManager.h
//  Communicator
//
//  Created by Mushrankhan Pathan on 21/10/21.
//  Copyright © 2021 Ribbideo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GlobalManager : NSObject

typedef void (^ ActionBlock)(void);

+(UIViewController*)currentController;
+(UIViewController*)currentController:(UIViewController*) baseController;

@end

NS_ASSUME_NONNULL_END

GlobalManager.m

//
//  GlobalManager.m
//  Communicator
//
//  Created by Mushrankhan Pathan on 21/10/21.
//  Copyright © 2021 Ribbideo. All rights reserved.
//

#import "GlobalManager.h"

@implementation GlobalManager

+(UIViewController*)currentController
{
    UIViewController *base = UIApplication.sharedApplication.keyWindow.rootViewController;
    return [GlobalManager currentController:base];
}

+(UIViewController*)currentController:(UIViewController*) baseController
{
    if ([baseController isKindOfClass:[UINavigationController class]]) {
        return [GlobalManager currentController:((UINavigationController*)baseController).visibleViewController];
    }
    
    if ([baseController isKindOfClass:[UITabBarController class]]) {
        UINavigationController* moreNavigationController = ((UITabBarController*)baseController).moreNavigationController;
        UIViewController* top = moreNavigationController.topViewController;
        if (top.view.window != nil) {
            return [GlobalManager currentController:top];
        }
        UIViewController* selectedViewController = ((UITabBarController*)baseController).selectedViewController;
        if (selectedViewController != nil) {
            return [GlobalManager currentController:selectedViewController];
        }
    }
    
    if (baseController.presentedViewController != nil) {
        return [GlobalManager currentController:baseController.presentedViewController];
    }
    
    return baseController;
}

@end

How to use.

UIViewController *currentVC = [GlobalManager currentController];
Mushrankhan
  • 749
  • 9
  • 12