Rectangle 27 89

rootViewController
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 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...

The problem with this is if the visible view does not belong to the root view controller (in the case of modal views and such).

Yes, I do. But it maybe a UITabViewController. Isn't there a directly method to get the UIViewController on the screen?

well, you see, UINavigationController provides a way for you to know which controller is topmost; your root controller should provide the same info some way. It cannot be inferred in general because it depends strictly on how you built your UI and there is no explicit controller hierarchy (like it happens for views). You may simply add a property to your root controller and set its value whenever you "push" a new controller on top.

As long as the value is kept up to date, that seems like a good way to go to me too.

There is no direct way of getting to the controller from a UIView instance. rootViewController is not necessarily the currently shown controller. It's just at the top of the view hierarchy.

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 83

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.

#import <UIKit/UIKit.h>

@interface UIWindow (PazLabs)

- (UIViewController *) visibleViewController;

@end
#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
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
            }
        }
    }
}

how can i use this for swift version?

I just posted an answer with the same approach as in this answer except for an updated syntax: It's using a switch-case and follows the Swift 3 naming conventions: stackoverflow.com/a/42486823/3451975

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 37

You could also post a notification via NSNotificationCenter. This let's you deal with a number of situations where traversing the view controller hierarchy might be tricky - for example when modals are being presented, etc.

// MyAppDelegate.h
NSString * const UIApplicationDidReceiveRemoteNotification;

// MyAppDelegate.m
NSString * const UIApplicationDidReceiveRemoteNotification = @"UIApplicationDidReceiveRemoteNotification";

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    [[NSNotificationCenter defaultCenter]
     postNotificationName:UIApplicationDidReceiveRemoteNotification
     object:self
     userInfo:userInfo];
}
-(void)viewDidLoad {
    [[NSNotificationCenter defaultCenter] 
      addObserver:self
      selector:@selector(didReceiveRemoteNotification:)                                                  
      name:UIApplicationDidReceiveRemoteNotification
      object:nil];
}

-(void)viewDidUnload {
    [[NSNotificationCenter defaultCenter] 
      removeObserver:self
      name:UIApplicationDidReceiveRemoteNotification
      object:nil];
}

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
    // see http://stackoverflow.com/a/2777460/305149
   if (self.isViewLoaded && self.view.window) {
      // handle the notification
   }
}

You could also use this approach to instrument controls which need to update when a notification is received and are used by several view controllers. In that case, handle the add/remove observer calls in the init and dealloc methods, respectively.

addObserver:bar
viewDidLoad
self

Thanks for pointing that out - it should be self. I'll update the answer.

crash while getting all keys from userInfo.. Any idea? [NSConcreteNotification allKeys]: unrecognized selector sent to instance 0x1fd87480 2013-07-05 16:10:36.469 Providence[2961:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteNotification allKeys]: unrecognized selector sent to instance 0x1fd87480'

@AwaisTariq - Hmmm - my guess is that the object passed by iOS to didReceiveRemoteNotification is not actually an NSDictionary, as the interface specifies.

What if the user has not navigated yet to your observer class? :/

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 34

UITabBarController
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
    }
}
if let rootViewController = UIApplication.topViewController() {
        //do sth with root view controller
    }

Really nice solution here, works like a charm!

This appears to be code for Swift 2.x. Swift 3.x does not have "where" anymore. Also, "sharedApplication()" is now "shared". Not a big deal. It only takes a minute to update. Might be good to mention that it uses recursion. Also, each call to topViewController should need the "base:" prefix.

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 13

I have found that iOS 8 has screwed everything up. In iOS 7 there is a new UITransitionView on the view hierarchy whenever you have a modally presented UINavigationController. Anyway, here's my code that finds gets the topmost VC. Calling getTopMostViewController should return a VC that you should be able to send a message like presentViewController:animated:completion. It's purpose is to get you a VC that you can use to present a modal VC, so it will most likely stop and return at container classes like UINavigationController and NOT the VC contained within them. Should not be hard to adapt the code to do that too. I've tested this code in various situations in iOS 6, 7 and 8. Please let me know if you find bugs.

+ (UIViewController*) getTopMostViewController
{
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    if (window.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for(window in windows) {
            if (window.windowLevel == UIWindowLevelNormal) {
                break;
            }
        }
    }

    for (UIView *subView in [window subviews])
    {
        UIResponder *responder = [subView nextResponder];

        //added this block of code for iOS 8 which puts a UITransitionView in between the UIWindow and the UILayoutContainerView
        if ([responder isEqual:window])
        {
            //this is a UITransitionView
            if ([[subView subviews] count])
            {
                UIView *subSubView = [subView subviews][0]; //this should be the UILayoutContainerView
                responder = [subSubView nextResponder];
            }
        }

        if([responder isKindOfClass:[UIViewController class]]) {
            return [self topViewController: (UIViewController *) responder];
        }
    }

    return nil;
}

+ (UIViewController *) topViewController: (UIViewController *) controller
{
    BOOL isPresenting = NO;
    do {
        // this path is called only on iOS 6+, so -presentedViewController is fine here.
        UIViewController *presented = [controller presentedViewController];
        isPresenting = presented != nil;
        if(presented != nil) {
            controller = presented;
        }

    } while (isPresenting);

    return controller;
}

Please don't duplicate answers - either flag the questions as duplicates if they are, or answer the individual questions with the specific answer they deserve if they aren't duplicates.

Thanks,It works.It should be better to have a class finish this job with static method or just function,so the user can copy and paste.

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 9

- (UIViewController *)getTopViewController {
    UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
    while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController;

    return topViewController;
}
func getTopViewController() -> UIViewController {
    var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil) {
        topViewController = topViewController.presentedViewController!
    }
    return topViewController
}

Works anywhere in your app, even with modals.

This does not handle the situation where the presented view controller is a UINavigationController which has its own children.

@levigroker, perhaps it's the way you've architected your views? It works fine for me to use this with a Nav. (that's how I'm using it)

@jungledev I'm sure you're correct. That said, a solution which works in all view controller configurations is what is needed.

@levigroker it does work in all standard vc configurations- the app I work on has a really complex architecture, is used by over 500k users, and this works everywhere in the app. Perhaps you should post a question asking why it doesn't work in your view, with code examples?

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 9

extension UIWindow {
    /// Returns the currently visible view controller if any reachable within the window.
    public var visibleViewController: UIViewController? {
        return UIWindow.visibleViewController(from: rootViewController)
    }

    /// Recursively follows navigation controllers, tab bar controllers and modal presented view controllers starting
    /// from the given view controller to find the currently visible view controller.
    ///
    /// - Parameters:
    ///   - viewController: The view controller to start the recursive search from.
    /// - Returns: The view controller that is most probably visible on screen right now.
    public static func visibleViewController(from viewController: UIViewController?) -> UIViewController? {
        switch viewController {
        case let navigationController as UINavigationController:
            return UIWindow.visibleViewController(from: navigationController.visibleViewController ?? navigationController.topViewController)

        case let tabBarController as UITabBarController:
            return UIWindow.visibleViewController(from: tabBarController.selectedViewController)

        case let presentingViewController where viewController?.presentedViewController != nil:
            return UIWindow.visibleViewController(from: presentingViewController?.presentedViewController)

        default:
            return viewController
        }
    }
}

The basic idea is the same as in zirinisp's answer, it's just using a more Swift 3 like syntax.

UIWindowExtension.swift
import UIKit

copy the above extension code

if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
    // do whatever you want with your `visibleViewCtrl`
}

Or if you know your visible view controller is reachable from a specific view controller:

if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
    // do whatever you want with your `visibleViewCtrl`
}

The third case will crash because of an infinite recursion. The fix is to rename the vc as presentingViewController and pass presentingViewController.presentedViewController as the parameter to the recursive method.

UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
presentedViewController
viewController
case let presentingViewController where viewController?.presentedViewController != nil:  return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)

Works perfectly ! You re the man ! :)

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 7

Specify title to each ViewController and then get the title of current ViewController by the code given below.

-(void)viewDidUnload {
  NSString *currentController = self.navigationController.visibleViewController.title;
if([currentController isEqualToString:@"myViewControllerTitle"]){
    //write your code according to View controller.
  }
}

Dfntly the best answer, also u can name your viewController with: self.title = myPhotoView

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 7

zirinisp's Answer in Swift:

extension UIWindow {

    func visibleViewController() -> UIViewController? {
        if let rootViewController: UIViewController  = self.rootViewController {
            return UIWindow.getVisibleViewControllerFrom(rootViewController)
        }
        return nil
    }

    class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {

        if vc.isKindOfClass(UINavigationController.self) {

            let navigationController = vc as UINavigationController
            return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)

        } else if vc.isKindOfClass(UITabBarController.self) {

            let tabBarController = vc as UITabBarController
            return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!)

        } else {

            if let presentedViewController = vc.presentedViewController {

                return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

            } else {

                return vc;
            }
        }
    }
}
if let topController = window.visibleViewController() {
            println(topController)
        }
as!
navigationController.visibleViewController!

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 4

Why not just handle the push notification code in the app delegate? Is it directly related to a view?

You can check if a UIViewController's view is currently visible by checking if it's view's window property has a value. See more here.

Yes, it is related to a view, as i have to show the badge view. let me check the link. thank you :)

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 3

Regarding NSNotificationCenter Post above (sorry can't find out where to post a comment under it...)

In case some were getting the -[NSConcreteNotification allKeys] error of sorts. Change this:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
-(void)didReceiveRemoteNotification:(NSNotification*)notif {
NSDictionary *dict = notif.userInfo;
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 3

Create a file, name it UIWindowExtension.swift and paste the following snippet:

import UIKit

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
            }
        }
    }
}

func getTopViewController() -> UIViewController? {
    let appDelegate = UIApplication.sharedApplication().delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}
if let topVC = getTopViewController() {

}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 2

This worked for me. I have many targets that have different controllers so previous answers didn't seemed to work.

var window: UIWindow?

then, in your function

let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
    if activeController.isKindOfClass( MyViewController )  {
        println("I have found my controller!")    
   }
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 1

func getTopViewController() -> UIViewController {
    var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil) {
        topViewController = topViewController.presentedViewController!
    }
    return topViewController
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 1

extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}

With this you can easily get the top post view controller like so

let viewController = UIApplication.topMostViewController

One thing to note is that if there's a UIAlertController currently being displayed, UIApplication.topMostViewController will return a UIAlertController.

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 1

This is the best possible way that I have tried out. If it should help anyone...

+ (UIViewController*) topMostController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    return topController;
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 0

Simple extension for UIApplication in Swift:

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 {
            if let selected = tab.selectedViewController {
                return topViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topViewController(base: presented)
        }
        return base
    }
}
if let rootViewController = UIApplication.topViewController() {
        //do sth with root view controller
    }

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 0

Regarding NSNotificationCenter Post above (sorry can't find out where to post a comment under it...)

In case some were getting the -[NSConcreteNotification allKeys] error of sorts. Change this:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
-(void)didReceiveRemoteNotification:(NSNotification*)notif {
NSDictionary *dict = notif.userInfo;
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 0

func getTopViewController() -> UIViewController {
    var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil) {
        topViewController = topViewController.presentedViewController!
    }
    return topViewController
}

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification
Rectangle 27 0

extension UIWindow {
    /// Returns the currently visible view controller if any reachable within the window.
    public var visibleViewController: UIViewController? {
        return UIWindow.visibleViewController(from: rootViewController)
    }

    /// Recursively follows navigation controllers, tab bar controllers and modal presented view controllers starting
    /// from the given view controller to find the currently visible view controller.
    ///
    /// - Parameters:
    ///   - viewController: The view controller to start the recursive search from.
    /// - Returns: The view controller that is most probably visible on screen right now.
    public static func visibleViewController(from viewController: UIViewController?) -> UIViewController? {
        switch viewController {
        case let navigationController as UINavigationController:
            return UIWindow.visibleViewController(from: navigationController.visibleViewController ?? navigationController.topViewController)

        case let tabBarController as UITabBarController:
            return UIWindow.visibleViewController(from: tabBarController.selectedViewController)

        case let presentingViewController where viewController?.presentedViewController != nil:
            return UIWindow.visibleViewController(from: presentingViewController?.presentedViewController)

        default:
            return viewController
        }
    }
}

The basic idea is the same as in zirinisp's answer, it's just using a more Swift 3 like syntax.

UIWindowExtension.swift
import UIKit

copy the above extension code

if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
    // do whatever you want with your `visibleViewCtrl`
}

Or if you know your visible view controller is reachable from a specific view controller:

if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
    // do whatever you want with your `visibleViewCtrl`
}

The third case will crash because of an infinite recursion. The fix is to rename the vc as presentingViewController and pass presentingViewController.presentedViewController as the parameter to the recursive method.

UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
presentedViewController
viewController
case let presentingViewController where viewController?.presentedViewController != nil:  return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)

Works perfectly ! You re the man ! :)

iphone - Get the current displaying UIViewController on the screen in ...

iphone ios uiviewcontroller push-notification