Rectangle 27 0

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


extension UIApplication {
    var visibleViewController : UIViewController? {
        return keyWindow?.rootViewController?.topViewController
    }
}

extension UIViewController {
    fileprivate var topViewController: UIViewController {
        switch self {
        case is UINavigationController:
            return (self as! UINavigationController).visibleViewController?.topViewController ?? self
        case is UITabBarController:
            return (self as! UITabBarController).selectedViewController?.topViewController ?? self
        default:
            return presentedViewController?.topViewController ?? self
        }
    }
}
Note
Rectangle 27 0

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


UINavigationController
UIViewController *vc = self.window.rootViewController;
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
rootViewController

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

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

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.

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

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.

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

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

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

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.

Note
Rectangle 27 0

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


UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
UIWindowExtension.swift
case let presentingViewController where viewController?.presentedViewController != nil:  return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
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
        }
    }
}
if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
    // do whatever you want with your `visibleViewCtrl`
}
if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
    // do whatever you want with your `visibleViewCtrl`
}
import UIKit
presentedViewController
viewController

copy the above extension code

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

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

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.

Works perfectly ! You re the man ! :)

Note
Rectangle 27 0

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


+ (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;
}

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.

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.

Note
Rectangle 27 0

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


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

@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?

@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)

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

Works anywhere in your app, even with modals.

Note
Rectangle 27 0

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


-(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
   }
}
// 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];
}
addObserver:bar
self
viewDidLoad

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

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

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

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.

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.

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'

Note
Rectangle 27 0

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


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
        }
    }
}
let viewController = UIApplication.topMostViewController

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

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

Note
Rectangle 27 0

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


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

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

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.

Note
Rectangle 27 0

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


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

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

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

Note
Rectangle 27 0

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


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

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

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

Note