Obtenir le contrôleur de vue actuel de l'app delegate (modal est possible)

je sais que pour obtenir le contrôleur de vue actuel de l'app delegate, je peux utiliser la propriété navigationController que j'ai configurée pour mon app. Cependant, il est possible que dans de nombreux endroits de mon application, un contrôleur de navigation modale ait pu être présenté. Y a-t-il un moyen de détecter cela de la part de l'app delegate, puisque le contrôleur de navigation actuel sera différent de celui auquel l'app delegate détient une référence?

7
demandé sur Mason 2013-12-10 07:39:24

10 réponses

je vous suggère D'utiliser NSNofiticationCenter.

//in AppDelegate:
@interface AppDelegate()
{
    ...
    id lastViewController;
    ...
}

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCurrentViewController) name:@"CurrentViewController" object:nil];
    ...
}

- (void)handleCurrentViewController:(NSNotification *)notification {
    if([[notification userInfo] objectForKey:@"lastViewController"]) {
        lastViewController = [[notification userInfo] objectForKey:@"lastViewController"];
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{    
    NSLog(@"last view controller is %@", [(UIViewController *)lastViewController class]);
}
@end

//in every ViewController you want to detect
@implementation SomeViewController
...
- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"CurrentViewController" object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self, @"lastViewController", nil]];
}
...
@end
7
répondu ziggear 2013-12-10 05:48:30

basé sur le gist ici , j'ai fait une catégorie pour obtenir le contrôleur de vue le plus haut, de sorte que l'appel [[UIApplication sharedApplication] topMostViewController] vous donnera le contrôleur de vue le plus haut dans votre application.

ceci est particulièrement utile dans iOS 8 où UIAlertView et UIActionSheet ont été dépréciés en faveur de UIAlertController , qui doit être présenté sur le contrôleur de vue le plus haut.

UIViewController+TopMostViewController.h

#import <UIKit/UIKit.h>

@interface UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

@interface UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

UIViewController+TopMostViewController.m

#import "UIViewController+TopMostViewController.h"

@implementation UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController
{
    if (self.presentedViewController == nil)
    {
        return self;
    }
    else if ([self.presentedViewController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
        UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
        return [lastViewController topMostViewController];
    }

    UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;
    return [presentedViewController topMostViewController];
}

@end

#pragma mark -

@implementation UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController
{
    return [self.keyWindow.rootViewController topMostViewController];
}

@end
9
répondu junjie 2014-12-02 06:43:06

ça a marché pour moi. J'ai beaucoup d'objectifs différents contrôleurs des réponses précédentes n'a pas semblé fonctionner.

tout d'abord, vous voulez cela dans votre classe AppDelegate:

var window: UIWindow?

puis, dans votre fonction

let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
    if activeController.isKindOfClass( MyViewController )  {
        println("I have found my controller!")    
   }
}
4
répondu CodeOverRide 2015-05-12 22:38:06

vous pouvez essayer d'obtenir le plus haut vue en faisant:

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

bien que ce point de vue puisse être invisible ou même couvert par certains de ses subviews...

cela dépend de votre UI mais cela pourrait aider.

3
répondu Milo 2013-12-10 03:53:09

grande solution dans Swift, mettre en œuvre dans AppDelegate

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}

Objectif-C

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
       UITabBarController* tabBarController = (UITabBarController*)rootViewController;
       return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
      UINavigationController* navigationController = (UINavigationController*)rootViewController;
      return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
     } else if (rootViewController.presentedViewController) {
       UIViewController* presentedViewController = rootViewController.presentedViewController;
       return [self topViewControllerWithRootViewController:presentedViewController];
     } else {
       return rootViewController;
    }
}
3
répondu Edward Novelo 2015-07-03 22:50:15

si vous avez le contrôleur de navigation dans L'App Delegate, il vous suffit d'utiliser la propriété visibleViewController . Il vous donnera le visible contrôleur, même si c'est un modal.

1
répondu hukir 2013-12-10 03:54:15

dans swift, vous pouvez obtenir le ViewController actif comme ceci:

 let navigationController = application.windows[0].rootViewController as UINavigationController

 let activeViewCont = navigationController.visibleViewController
1
répondu jmcastel 2015-01-07 09:41:00

la meilleure solution, fonctionne aussi avec moreNavigationController dans UITabBarController :

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
    }
}
0
répondu Bartłomiej Semańczyk 2015-07-22 10:53:43

ci-dessous le code fonctionne très bien.

+(UIViewController*) findBestViewController:(UIViewController*)vc {

if (vc.presentedViewController) {

    // Return presented view controller
    return [AppDelegate findBestViewController:vc.presentedViewController];

} else if ([vc isKindOfClass:[UISplitViewController class]]) {

    // Return right hand side
    UISplitViewController* svc = (UISplitViewController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.viewControllers.lastObject];
    else
        return vc;

} else if ([vc isKindOfClass:[UINavigationController class]]) {

    // Return top view
    UINavigationController* svc = (UINavigationController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.topViewController];
    else
        return vc;

} else if ([vc isKindOfClass:[UITabBarController class]]) {

    // Return visible view
    UITabBarController* svc = (UITabBarController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.selectedViewController];
    else
        return vc;

} else {

    // Unknown view controller type, return last child view controller
    return vc;

}

}

+(UIViewController*) currentViewController {

// Find best view controller
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
return [AppDelegate findBestViewController:viewController];

}

0
répondu umitcel 2016-02-21 14:12:33

les autres solutions ci-dessus ne fonctionnent que partiellement car les hiérarchies de vues complexes ne sont pas gérées (le contrôleur de navigation intégré dans le contrôleur de la barre d'onglets, les contrôleurs de vue fractionnée, les conteneurs de vue et aussi les contrôleurs d'alerte pourraient tout gâcher).

Je résous cela en gardant une référence du contrôleur de vue actuel dans AppDelegate. Chaque fois que la vue apparaît je profite de viewDidAppear(animated:) et placer la référence dans le délégué app.

je sais que la question concernait L'Objectif-C, mais je ne peux fournir que du code Swift et je suis sûr qu'il sera utile aux deux types d'utilisateurs.

D'abord: j'ai mis en place un protocole pour garder les choses propres et réutilisables:

protocol UpdatableViewController {
    func updateUI()
}

Second: j'ai ajouté une référence à AppDelegate:

var currentViewController: UpdatableViewController?

troisième: j'ai réglé le contrôleur de vue courant dans viewDidAppear ():

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
    appDelegate?.currentViewController = self
}

quatrième:

extension ViewController1: UpdatableViewController {
    func updateUI() {
        print("Implement updating here")
    }
}
0
répondu Andrej 2016-08-16 14:59:38