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.
11 Answers
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.

- 36,899
- 45
- 162
- 260
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

- 368
- 6
- 18

- 6,976
- 5
- 45
- 62
If you have UINavigationController into appDelegate then use its property topViewController or visibleViewController

- 29,669
- 15
- 106
- 125
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
}

- 3,421
- 2
- 37
- 41

- 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
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;
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 :)

- 381
- 3
- 7
For anyone not using a UINavigationController
but 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

- 4,960
- 2
- 37
- 45
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
}

- 3,209
- 1
- 34
- 40
-
Awesome!! Its working for Tabbar also because I was searching for Tabbar only – Darshit Mendapara Jun 29 '19 at 13:28
Swift Solution:
self.window.rootViewController.presentedViewController.
That should get you what you need.

- 8,222
- 5
- 68
- 78

- 769
- 6
- 9
-
This approach only works if self is a UIView or similar, not if it is an abstract class. – Duncan Babbage Jul 01 '18 at 04:56
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()

- 614
- 7
- 9
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];

- 749
- 9
- 12