Get the current displaying UIViewController on the screen in AppDelegate.m Get the current displaying UIViewController on the screen in AppDelegate.m ios ios

Get the current displaying UIViewController on the screen in AppDelegate.m


I always love solutions that involve categories as they are bolt on and can be easily reused.

So I created a category on UIWindow. You can now call visibleViewController on UIWindow and this will get you the visible view controller by searching down the controller hierarchy. This works if you are using navigation and/or tab bar controller. If you have another type of controller to suggest please let me know and I can add it.

UIWindow+PazLabs.h (header file)

#import <UIKit/UIKit.h>@interface UIWindow (PazLabs)- (UIViewController *) visibleViewController;@end

UIWindow+PazLabs.m (implementation file)

#import "UIWindow+PazLabs.h"@implementation UIWindow (PazLabs)- (UIViewController *)visibleViewController {    UIViewController *rootViewController = self.rootViewController;    return [UIWindow getVisibleViewControllerFrom:rootViewController];}+ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {    if ([vc isKindOfClass:[UINavigationController class]]) {        return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];    } else if ([vc isKindOfClass:[UITabBarController class]]) {        return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];    } else {        if (vc.presentedViewController) {            return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];        } else {            return vc;        }    }}@end

Swift Version

public extension UIWindow {    public var visibleViewController: UIViewController? {        return UIWindow.getVisibleViewControllerFrom(self.rootViewController)    }    public static func getVisibleViewControllerFrom(_ vc: UIViewController?) -> UIViewController? {        if let nc = vc as? UINavigationController {            return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)        } else if let tc = vc as? UITabBarController {            return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)        } else {            if let pvc = vc?.presentedViewController {                return UIWindow.getVisibleViewControllerFrom(pvc)            } else {                return vc            }        }    }}


You can use the rootViewController also when your controller is not a UINavigationController:

UIViewController *vc = self.window.rootViewController;

Once you know the root view controller, then it depends on how you have built your UI, but you can possibly find out a way to navigate through the controllers hierarchy.

If you give some more details about the way you defined your app, then I might give some more hint.

EDIT:

If you want the topmost view (not view controller), you could check

[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

although this view might be invisible or even covered by some of its subviews...

again, it depends on your UI, but this might help...


Simple extension for UIApplication in Swift (cares even about moreNavigationController within UITabBarController on iPhone):

extension UIApplication {    class func topViewController(base: UIViewController? = UIApplication.sharedApplication().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 where top.view.window != nil {                return topViewController(top)            } else if let selected = tab.selectedViewController {                return topViewController(selected)            }        }        if let presented = base?.presentedViewController {            return topViewController(base: presented)        }        return base    }}

Simple usage:

    if let rootViewController = UIApplication.topViewController() {        //do sth with root view controller    }

Works perfect:-)

UPDATE for clean code:

extension UIViewController {    var top: UIViewController? {        if let controller = self as? UINavigationController {            return controller.topViewController?.top        }        if let controller = self as? UISplitViewController {            return controller.viewControllers.last?.top        }        if let controller = self as? UITabBarController {            return controller.selectedViewController?.top        }        if let controller = presentedViewController {            return controller.top        }        return self    }}