137 votes

Obtenir le UIViewController actuellement affiché à l'écran dans AppDelegate.m

L'actuel UIViewController sur l'écran doivent répondre aux notifications push des APN, en définissant des vues de badges. Mais comment puis-je obtenir le UIViewController en méthode application:didReceiveRemoteNotification : de AppDelegate.m ?

J'ai essayé d'utiliser self.window.rootViewController pour obtenir l'affichage actuel UIViewController il peut s'agir d'un UINavigationViewController ou un autre type de contrôleur de vue. Et je découvre que le visibleViewController propriété de UINavigationViewController peut être utilisé pour obtenir le UIViewController sur l'écran. Mais que faire s'il ne s'agit pas d'une UINavigationViewController ?

Toute aide est appréciée ! Le code correspondant est le suivant.

AppDelegate.m

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

    //I would like to find out which view controller is on the screen here.

    UIViewController *vc = [(UINavigationViewController *)self.window.rootViewController visibleViewController];
    [vc performSelector:@selector(handleThePushNotification:) withObject:userInfo];
}
...

ViewControllerA.m

- (void)handleThePushNotification:(NSDictionary *)userInfo{

    //set some badge view here

}

14voto

nvrtd frst Points 1903

J'ai découvert que iOS 8 a tout gâché. Dans iOS 7, il y a un nouveau UITransitionView sur la hiérarchie des vues chaque fois que vous avez une présentation modale UINavigationController . Quoi qu'il en soit, voici mon code qui trouve le VC le plus haut. Appel à getTopMostViewController devrait retourner un VC que vous devriez pouvoir envoyer un message du type presentViewController:animated:completion . Son but est d'obtenir un VC que vous pouvez utiliser pour présenter un VC modal, donc il s'arrêtera très probablement et retournera aux classes de conteneurs comme UINavigationController et NON le VC qu'ils contiennent. Il ne devrait pas être difficile d'adapter le code pour faire cela aussi. J'ai testé ce code dans diverses situations sous iOS 6, 7 et 8. Veuillez me faire savoir si vous trouvez des bogues.

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

14voto

jungledev Points 386

Beaucoup moins de code que toutes les autres solutions :

Version Objective-C :

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

    return topViewController;
}

Version Swift 2.0 : (le crédit revient à Steve.B)

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

Fonctionne partout dans votre application, même avec des modales.

8voto

Bobj-C Points 1182

Réponse de zirinisp dans 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;
            }
        }
    }
}

Utilisation :

 if let topController = window.visibleViewController() {
            println(topController)
        }

7voto

Neel Kamal Points 21

Spécifiez le titre de chaque ViewController et obtenez ensuite le titre du ViewController actuel par le code donné ci-dessous.

-(void)viewDidUnload {
  NSString *currentController = self.navigationController.visibleViewController.title;

Ensuite, vérifiez-le par votre titre comme ceci

  if([currentController isEqualToString:@"myViewControllerTitle"]){
    //write your code according to View controller.
  }
}

5voto

Nicolas Manzini Points 2205

Le mien est meilleur ! :)

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X