web-dev-qa-db-fra.com

iPhone - Comment trouver le meilleur contrôleur de vue

J'ai rencontré quelques cas maintenant où il serait pratique de pouvoir trouver le contrôleur de vue "le plus haut" (celui qui est responsable de la vue actuelle), mais je n'ai pas trouvé le moyen de le faire.

En gros, le défi est le suivant: étant donné que l’on exécutedans une classe qui n’est pas un contrôleur de vue(ou une vue)[et qui n’a pas l’adresse d’une vue active]et si l'adresse du contrôleur de vue le plus haut (ou, disons, l'adresse du contrôleur de navigation) n'a pas été transmise, est-il possible de trouver ce contrôleur de vue? (Et si oui, comment?)

Ou, à défaut, est-il possible de trouver la vue la plus haute?

230
Hot Licks

iOS 4 a introduit la propriété rootViewController sur UIWindow:

[UIApplication sharedApplication].keyWindow.rootViewController;

Vous devrez cependant le définir vous-même après avoir créé le contrôleur de vue.

77

Je pense que vous avez besoin d'une combinaison de la réponse acceptée et de @ fishstix

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

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

    return topController;
}

Swift 3.0+  

func topMostController() -> UIViewController? {
    guard let window = UIApplication.shared.keyWindow, let rootViewController = window.rootViewController else {
        return nil
    }

    var topController = rootViewController

    while let newTopController = topController.presentedViewController {
        topController = newTopController
    }

    return topController
}
397
Eric

Pour compléter le answer de JonasG (qui a laissé les contrôleurs de la barre d’onglet en traversant), voici ma version du retour du contrôleur de vue visible:

- (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;
    }
}
148
kleo

Une version complète non récursive, prenant en charge différents scénarios:

  • Le contrôleur de vue présente une autre vue
  • Le contrôleur de vue est une UINavigationController
  • Le contrôleur de vue est une UITabBarController

Objectif c

 UIViewController *topViewController = self.window.rootViewController;
 while (true)
 {
     if (topViewController.presentedViewController) {
         topViewController = topViewController.presentedViewController;
     } else if ([topViewController isKindOfClass:[UINavigationController class]]) {
         UINavigationController *nav = (UINavigationController *)topViewController;
         topViewController = nav.topViewController;
     } else if ([topViewController isKindOfClass:[UITabBarController class]]) {
         UITabBarController *tab = (UITabBarController *)topViewController;
         topViewController = tab.selectedViewController;
     } else {
         break;
     }
 }

Swift 4+

extension UIWindow {
    func topViewController() -> UIViewController? {
        var top = self.rootViewController
        while true {
            if let presented = top?.presentedViewController {
                top = presented
            } else if let nav = top as? UINavigationController {
                top = nav.visibleViewController
            } else if let tab = top as? UITabBarController {
                top = tab.selectedViewController
            } else {
                break
            }
        }
        return top
    }
}
40
Yuchen Zhong

Obtenir le meilleur contrôleur de vue pour Swift en utilisant des extensions

Code:  

extension UIViewController {
    @objc func topMostViewController() -> UIViewController {
        // Handling Modal views
        if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        // Handling UIViewController's added as subviews to some other views.
        else {
            for view in self.view.subviews
            {
                // Key property which most of us are unaware of / rarely use.
                if let subViewController = view.next {
                    if subViewController is UIViewController {
                        let viewController = subViewController as! UIViewController
                        return viewController.topMostViewController()
                    }
                }
            }
            return self
        }
    }
}

extension UITabBarController {
    override func topMostViewController() -> UIViewController {
        return self.selectedViewController!.topMostViewController()
    }
}

extension UINavigationController {
    override func topMostViewController() -> UIViewController {
        return self.visibleViewController!.topMostViewController()
    }
}

Utilisation:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()
27
Varuna

Pour compléter la réponse de answer d’Eric (qui a omis les popovers, les contrôleurs de navigation, les contrôleurs de tabulation, les contrôleurs de vue ajoutés en tant que sous-vues à d’autres contrôleurs de vue en traversant), voici ma version du retour du contrôleur de vue actuellement visible:

=============================================== ====================

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

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController {
    if ([viewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)viewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([viewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navContObj = (UINavigationController*)viewController;
        return [self topViewControllerWithRootViewController:navContObj.visibleViewController];
    } else if (viewController.presentedViewController && !viewController.presentedViewController.isBeingDismissed) {
        UIViewController* presentedViewController = viewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    }
    else {
        for (UIView *view in [viewController.view subviews])
        {
            id subViewController = [view nextResponder];
            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                if ([(UIViewController *)subViewController presentedViewController]  && ![subViewController presentedViewController].isBeingDismissed) {
                    return [self topViewControllerWithRootViewController:[(UIViewController *)subViewController presentedViewController]];
                }
            }
        }
        return viewController;
    }
}

=============================================== ====================

Et maintenant, tout ce que vous avez à faire pour obtenir le meilleur contrôleur de vue est d'appeler la méthode ci-dessus comme suit:

UIViewController *topMostViewControllerObj = [self topViewController];
24
Rajesh

Cette réponse inclut childViewControllers et maintient une implémentation propre et lisible.

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

    return [rootViewController topVisibleViewController];
}

- (UIViewController *)topVisibleViewController
{
    if ([self isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)self;
        return [tabBarController.selectedViewController topVisibleViewController];
    }
    else if ([self isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self;
        return [navigationController.visibleViewController topVisibleViewController];
    }
    else if (self.presentedViewController)
    {
        return [self.presentedViewController topVisibleViewController];
    }
    else if (self.childViewControllers.count > 0)
    {
        return [self.childViewControllers.lastObject topVisibleViewController];
    }

    return self;
}
19
Awesome-o

J'ai récemment eu cette situation dans l'un de mes projets, qui nécessitait d'afficher une vue de notification quel que soit le type de contrôleur affiché et son type (UINavigationController, contrôleur classique ou contrôleur de vue personnalisé), lorsque l'état du réseau était modifié.

Je viens donc de publier mon code, qui est assez simple et repose sur un protocole qui le rend flexible avec tous les types de contrôleurs de conteneurs .. Il semble être lié aux dernières réponses, mais de manière beaucoup plus flexible.

Vous pouvez récupérer le code ici: PPTopMostController

Et obtenu le plus haut contrôleur en utilisant 

UIViewController *c = [UIViewController topMostController];
13
ipodishima

C'est une amélioration à la réponse d'Eric:

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

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

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

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

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

_topMostController(UIViewController *cont) est une fonction d'assistance.

Il ne vous reste plus qu'à appeler topMostController() et le numéro le plus haut d'UIViewController doit être renvoyé!

11
JonasG
- (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;
    }
}
8
lifuqing_ios

Extension simple pour UIApplication dans Swift:

REMARQUE:

Il se préoccupe de moreNavigationController dans UITabBarController

extension UIApplication {

    class func topViewController(baseViewController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let navigationController = baseViewController as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }

        if let tabBarViewController = baseViewController as? UITabBarController {

            let moreNavigationController = tabBarViewController.moreNavigationController

            if let topViewController = moreNavigationController.topViewController where topViewController.view.window != nil {
                return topViewController(topViewController)
            } else if let selectedViewController = tabBarViewController.selectedViewController {
                return topViewController(selectedViewController)
            }
        }

        if let splitViewController = baseViewController as? UISplitViewController where splitViewController.viewControllers.count == 1 {
            return topViewController(splitViewController.viewControllers[0])
        }

        if let presentedViewController = baseViewController?.presentedViewController {
            return topViewController(presentedViewController)
        }

        return baseViewController
    }
}

Utilisation simple:

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

Voici mon point de vue sur ceci. Merci à @Stakenborg d’avoir indiqué comment éviter d’utiliser UIAlertView comme contrôleur supérieur

-(UIWindow *) returnWindowWithWindowLevelNormal
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for(UIWindow *topWindow in windows)
    {
        if (topWindow.windowLevel == UIWindowLevelNormal)
            return topWindow;
    }
    return [UIApplication sharedApplication].keyWindow;
}

-(UIViewController *) getTopMostController
{
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal)
    {
        topWindow = [self returnWindowWithWindowLevelNormal];
    }

    UIViewController *topController = topWindow.rootViewController;
    if(topController == nil)
    {
        topWindow = [UIApplication sharedApplication].delegate.window;
        if (topWindow.windowLevel != UIWindowLevelNormal)
        {
            topWindow = [self returnWindowWithWindowLevelNormal];
        }
        topController = topWindow.rootViewController;
    }

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

    if([topController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *nav = (UINavigationController*)topController;
        topController = [nav.viewControllers lastObject];

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

    return topController;
}
7
Kamran Khan
 @ implementation UIWindow (Extensions) 

- (UIViewController *) topMostController 
 {. UIViewController * topController = [self rootViewController]; 

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

 return topController; 
} 

 @ end 
7
FishStix

Pour la dernière version de Swift:
Créez un fichier, nommez-le UIWindowExtension.Swift et collez le fragment de code suivant:

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
}

Utilisez-le n'importe où comme:

if let topVC = getTopViewController() {

}
7
Ashok Kumar S

Voici ce qui a fonctionné pour moi. 

J'ai trouvé que parfois le contrôleur était nil sur la fenêtre de clé, comme la clé Windows était quelque chose de système d'exploitation comme une alerte, etc. 

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

     if (topController == nil)
     {
         // The windows in the array are ordered from back to front by window level; thus,
         // the last window in the array is on top of all other app windows.
         for (UIWindow *aWndow in [[UIApplication sharedApplication].windows reverseObjectEnumerator])
         {
             topController = aWndow.rootViewController;
             if (topController)
                 break;
         }
     }

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

     return topController;
 }
3
Tom Andersen

Solution alternative rapide:

static func topMostController() -> UIViewController {
    var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    while (topController?.presentedViewController != nil) {
        topController = topController?.presentedViewController
    }

    return topController!
}
3
Esqarrouth

En développant la réponse de @ Eric, vous devez faire attention à ce que la fenêtre keyWindow soit réellement la fenêtre que vous souhaitez. Si vous essayez d'utiliser cette méthode après avoir appuyé sur quelque chose dans une vue d'alerte, par exemple, la fenêtre keyWindow sera en réalité la fenêtre de l'alerte, ce qui ne fera aucun doute pour vous. Cela m'est arrivé à l’état sauvage lors de la gestion de liens profonds via une alerte et a causé des SIGABRT avec NO STACK TRACE. Chienne totale à déboguer.

Voici le code que j'utilise maintenant:

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

    UIViewController *topViewController = topWindow.rootViewController;

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

    return topViewController;
}

N'hésitez pas à mélanger cela avec n'importe quelle saveur de récupération du contrôleur de vue de dessus que vous aimez parmi les autres réponses à cette question.

3
Stakenborg

Cette solution est la plus complète. Il tient compte des éléments suivants:

L'exemple est dans Swift 3.

Il y a 3 surcharges

//Get the topmost view controller for the current application.
public func MGGetTopMostViewController() -> UIViewController?  {

    if let currentWindow:UIWindow = UIApplication.shared.keyWindow {
        return MGGetTopMostViewController(fromWindow: currentWindow)
    }

    return nil
}

//Gets the topmost view controller from a specific window.
public func MGGetTopMostViewController(fromWindow window:UIWindow) -> UIViewController? {

    if let rootViewController:UIViewController = window.rootViewController
    {
        return MGGetTopMostViewController(fromViewController:  rootViewController)
    }

    return nil
}


//Gets the topmost view controller starting from a specific UIViewController
//Pass the rootViewController into this to get the apps top most view controller
public func MGGetTopMostViewController(fromViewController viewController:UIViewController) -> UIViewController {

    //UINavigationController
    if let navigationViewController:UINavigationController = viewController as? UINavigationController {
        let viewControllers:[UIViewController] = navigationViewController.viewControllers
        if navigationViewController.viewControllers.count >= 1 {
            return MGGetTopMostViewController(fromViewController: viewControllers[viewControllers.count - 1])
        }
    }

    //UIPageViewController
    if let pageViewController:UIPageViewController = viewController as? UIPageViewController {
        if let viewControllers:[UIViewController] = pageViewController.viewControllers {
            if viewControllers.count >= 1 {
                return MGGetTopMostViewController(fromViewController: viewControllers[0])
            }
        }
    }

    //UITabViewController
    if let tabBarController:UITabBarController = viewController as? UITabBarController {
        if let selectedViewController:UIViewController = tabBarController.selectedViewController {
            return MGGetTopMostViewController(fromViewController: selectedViewController)
        }
    }

    //Lastly, Attempt to get the topmost presented view controller
    var presentedViewController:UIViewController! = viewController.presentedViewController
    var nextPresentedViewController:UIViewController! = presentedViewController?.presentedViewController

    //If there is a presented view controller, get the top most prensentedViewController and return it.
    if presentedViewController != nil {
        while nextPresentedViewController != nil {

            //Set the presented view controller as the next one.
            presentedViewController = nextPresentedViewController

            //Attempt to get the next presented view controller
            nextPresentedViewController = presentedViewController.presentedViewController
        }
        return presentedViewController
    }

    //If there is no topmost presented view controller, return the view controller itself.
    return viewController
}
3
Marc Renaud

Encore une autre solution Swift

func topController() -> UIViewController? {

    // recursive follow
    func follow(from:UIViewController?) -> UIViewController? {
        if let to = (from as? UITabBarController)?.selectedViewController {
            return follow(to)
        } else if let to = (from as? UINavigationController)?.visibleViewController {
            return follow(to)
        } else if let to = from?.presentedViewController {
            return follow(to)
        }
        return from
    }

    let root = UIApplication.sharedApplication().keyWindow?.rootViewController

    return follow(root)

}
3
Martin Algesten

Je pense que la plupart des réponses ont complètement ignoré UINavigationViewController, aussi j’ai traité ce cas d’utilisation avec l’implémentation suivante.

+ (UIViewController *)topMostController {
    UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController || [topController isMemberOfClass:[UINavigationController class]]) {
        if([topController isMemberOfClass:[UINavigationController class]]) {
            topController = [topController childViewControllers].lastObject;
        } else {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}
2
Aamir

Excellente solution dans Swift, implémenter 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
}
2
Edward Novelo

Et une autre solution Swift

extension UIViewController {
    static var topmostViewController: UIViewController? {
        return UIApplication.sharedApplication().keyWindow?.topmostViewController
    }

    var topmostViewController: UIViewController? {
        return presentedViewController?.topmostViewController ?? self
    }
}

extension UINavigationController {
    override var topmostViewController: UIViewController? {
        return visibleViewController?.topmostViewController
    }
}

extension UITabBarController {
    override var topmostViewController: UIViewController? {
        return selectedViewController?.topmostViewController
    }
}

extension UIWindow {
    var topmostViewController: UIViewController? {
        return rootViewController?.topmostViewController
    }
}
1
bzz

Deux fonctions ci-dessous peuvent aider à trouver le contrôleur topView sur la pile de contrôleurs de vue. Vous aurez peut-être besoin de personnalisation plus tard, mais pour ce code, il est bon de comprendre le concept de topViewController ou la pile de viewControllers.

- (UIViewController*)findTopViewController {

  id  topControler  = [self topMostController];

  UIViewController* topViewController;
  if([topControler isKindOfClass:[UINavigationController class]]) {
        topViewController = [[(UINavigationController*)topControler viewControllers] lastObject];
   } else if ([topControler isKindOfClass:[UITabBarController class]]) {
        //Here you can get reference of top viewcontroller from stack of viewcontrollers on UITabBarController
  } else {
        //topController is a preented viewController
        topViewController = (UIViewController*)topControler;
  }
    //NSLog(@"Top ViewController is: %@",NSStringFromClass([topController class]));
    return topViewController;
}

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

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    //NSLog(@"Top View is: %@",NSStringFromClass([topController class]));
    return topController;
}

Vous pouvez utiliser la méthode [viewController Class] pour connaître le type de classe d'un viewController.

1
iDevAmit

Rapide:

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

Usage:

 if let topController = window.visibleViewController() {
            println(topController)
        }
1
Bobj-C

Beaucoup de ces réponses sont incomplètes. Bien qu'il s'agisse d'Objective-C, il s'agit de la meilleure compilation que je puisse rassembler pour le moment, en tant que bloc non récursif:

UIViewController *(^topmostViewControllerForFrontmostNormalLevelWindow)(void) = ^UIViewController *{
    // NOTE: Adapted from various stray answers here:
    //   https://stackoverflow.com/questions/6131205/iphone-how-to-find-topmost-view-controller/20515681

    UIViewController *viewController;

    for (UIWindow *window in UIApplication.sharedApplication.windows.reverseObjectEnumerator.allObjects) {
        if (window.windowLevel == UIWindowLevelNormal) {
            viewController = window.rootViewController;
            break;
        }
    }

    while (viewController != nil) {
        if ([viewController isKindOfClass:[UITabBarController class]]) {
            viewController = ((UITabBarController *)viewController).selectedViewController;
        } else if ([viewController isKindOfClass:[UINavigationController class]]) {
            viewController = ((UINavigationController *)viewController).visibleViewController;
        } else if (viewController.presentedViewController != nil && !viewController.presentedViewController.isBeingDismissed) {
            viewController = viewController.presentedViewController;
        } else if (viewController.childViewControllers.count > 0) {
            viewController = viewController.childViewControllers.lastObject;
        } else {
            BOOL repeat = NO;

            for (UIView *view in viewController.view.subviews.reverseObjectEnumerator.allObjects) {
                if ([view.nextResponder isKindOfClass:[UIViewController class]]) {
                    viewController = (UIViewController *)view.nextResponder;

                    repeat = YES;
                    break;
                }
            }

            if (!repeat) {
                break;
            }
        }
    }

    return viewController;
};
1
Ben Guild

Utilisez l'extension ci-dessous pour récupérer la variable UIViewController actuelle visible. A travaillé pour Swift 4.0 et versions ultérieures

Code:

extension UIApplication {

    class func topViewController(_ viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = viewController as? UINavigationController {
            return topViewController(nav.visibleViewController)
        }
        if let tab = viewController as? UITabBarController {
            if let selected = tab.selectedViewController {
                return topViewController(selected)
            }
        }
        if let presented = viewController?.presentedViewController {
            return topViewController(presented)
        }
        return viewController
    }
}

Comment utiliser?

let objViewcontroller = UIApplication.topViewController()
1
iMHitesh Surani

Je ne sais pas si cela aidera ce que vous essayez d'accomplir en trouvant le contrôleur de vue le plus haut, mais j'essayais de présenter un nouveau contrôleur de vue, mais si mon contrôleur de vue racine avait déjà un dialogue modal, il serait bloqué. passerait au sommet de tous les contrôleurs de vue modale utilisant ce code:

UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}
1
Toland Hon

Pour éviter beaucoup de complexité, je garde un suivi du viewController actuel en créant un viewController dans le délégué et le règle lui-même à l'intérieur de chaque méthode viewDidLoad. Ainsi, chaque fois que vous chargerez une nouvelle vue, ViewController détenu dans le délégué correspondra à la vue view . C’est peut-être moche, mais cela fonctionne à merveille, et il n’est pas nécessaire d’avoir un contrôleur de navigation ni aucune de ces absurdités.

0
J_Tuck

Swift 4.2 Extension


extension UIApplication {

    class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {


            return topViewController(controller: presented)
        }
        return controller
    }
}

Utilisez-le de n'importe où,

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)

ou comme

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(attendanceViewController,
                                         animated: true)

S'adapte à toutes les classes comme UINavigationController, UITabBarController

Prendre plaisir!

0
Saranjith

Si le contrôleur racine est un contrôleur de navigation, la méthode correcte pour rechercher le contrôleur visible supérieur est la suivante:

UIViewController *rootVC = [[UIApplication sharedApplication] keyWindow].rootViewController;
if ([rootVC respondsToSelector:@selector(visibleViewController)])
{
    UIViewController *topVC = [(UINavigationController *)rootVC visibleViewController];
    // do your thing with topVC
}

Voici un extrait de UINavigationController.h:

@property(nonatomic,readonly,retain) UIViewController *topViewController; // The top view controller on the stack.
@property(nonatomic,readonly,retain) UIViewController *visibleViewController; // Return modal view controller if it exists. Otherwise the top view controller.
0
amit

Cela fonctionne très bien pour trouver la vue de dessus Controller 1 depuis n’importe quel contrôleur de vue racine

+ (UIViewController *)topViewControllerFor:(UIViewController *)viewController
{
    if(!viewController.presentedViewController)
        return viewController;
    return [MF5AppDelegate topViewControllerFor:viewController.presentedViewController];
}

/* View Controller for Visible View */

AppDelegate *app = [UIApplication sharedApplication].delegate;
UIViewController *visibleViewController = [AppDelegate topViewControllerFor:app.window.rootViewController]; 
0
johnnyg17

Mon problème était un peu différent. J'utilise SWRevealViewController dans mon application .J'ai utilisé answer de Yuchen Zhong, mais renvoie toujours topViewController en tant que SWRevealViewController. Pour ceux qui utilisent SWRevealViewController ou un autre module pour développer sideMenu. Voici mon extension à la réponse de Yuchen Zhong:

extension UIApplication {
class func topViewController() -> UIViewController? {
    var topVC = shared.keyWindow!.rootViewController
    while true {
        if let presented = topVC?.presentedViewController {
            topVC = presented
        } else if let nav = topVC as? UINavigationController {
            topVC = nav.visibleViewController
        } else if let tab = topVC as? UITabBarController {
            topVC = tab.selectedViewController
        }else if let swRVC = topVC as? SWRevealViewController {
            topVC = swRVC.frontViewController
        } else {
            break
        }
    }
    return topVC
}
}
0
Gulfam Khan

La réponse précédente ne semble pas traiter les cas où rootController est UITabBarController ou UINavigationController.

Voici la fonction dans Swift qui fonctionne pour ces cas:

func getCurrentView() -> UIViewController?
{
    if let window = UIApplication.sharedApplication().keyWindow, var currentView: UIViewController = window.rootViewController
    {
        while (currentView.presentedViewController != nil)
        {
            if let presented = currentView.presentedViewController
            {
                currentView = presented
            }
        }

        if currentView is UITabBarController
        {
            if let visible = (currentView as! UITabBarController).selectedViewController
            {
                currentView = visible;
            }
        }

        if currentView is UINavigationController
        {
            if let visible = (currentView as! UINavigationController).visibleViewController
            {
                currentView = visible;
            }
        }

        return currentView
    }

    return nil
}
0
Dragouf

Je pense que peut-être une chose est négligée ici. Il est peut-être préférable de passer le viewController parent à la fonction qui utilise le viewController. Si vous parcourez la hiérarchie de vues pour rechercher le contrôleur de la vue de dessus, il viole probablement la séparation entre la couche modèle et la couche d'interface utilisateur et constitue une odeur de code. Juste en le soulignant, j'ai fait la même chose, puis j'ai réalisé qu'il était beaucoup plus simple de le passer pour qu'il fonctionne, en renvoyant l'opération de modèle à la couche d'interface utilisateur dans laquelle j'ai une référence au contrôleur de vue. 

0
possen

vous pouvez trouver le plus haut contrôleur de vue en utilisant

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];
0
Tapas Pal

Voici l'implémentation par Swift d'une application avec UINavigationController comme racine. 

  if let nav = UIApplication.sharedApplication().keyWindow?.rootViewController as? UINavigationController{
        //get the current's navigation view controller
        var vc = nav.topViewController
        while vc?.presentedViewController != nil {
            vc = vc?.presentedViewController
        }
        return vc
    }
0

Je pense que la solution de Rajesh est presque parfaite, mais je pense qu'il vaut mieux parcourir les vues secondaires de haut en bas.

+ (UIViewController *)topViewController:(UIViewController *)viewController{

    if (viewController.presentedViewController)
    {

            UIViewController *presentedViewController = viewController.presentedViewController;
            return [self topViewController:presentedViewController];
     } 
     else if ([viewController isKindOfClass:[UITabBarController class]])
     {

            UITabBarController *tabBarController = (UITabBarController *)viewController;
            return [self topViewController:tabBarController.selectedViewController];
    }

         else if ([viewController isKindOfClass:[UINavigationController class]])
    {   

            UINavigationController *navController = (UINavigationController *)viewController;

            return [self topViewController:navController.visibleViewController];
    }

    // Handling UIViewController's added as subviews to some other views.
    else {

        NSInteger subCount = [viewController.view subviews].count - 1;

        for (NSInteger index = subCount; index >=0 ; --index)
        {

            UIView *view = [[viewController.view subviews] objectAtIndex:index];

            id subViewController = [view nextResponder];    // Key property which most of us are unaware of / rarely use.

            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                return [self topViewController:subViewController];
            }
        }
        return viewController;
    }
}
0
Siguang Zhang

Une autre solution repose sur la chaîne de réponse, qui peut ou non fonctionner en fonction de la nature du premier intervenant

  1. Obtenez le premier répondant .
  2. Récupère le UIViewController associé à ce premier intervenant .

Exemple de pseudo-code:

+ (UIViewController *)currentViewController {
    UIView *firstResponder = [self firstResponder]; // from the first link above, but not guaranteed to return a UIView, so this should be handled more appropriately.
    UIViewController *viewController = [firstResponder viewController]; // from the second link above
    return viewController;
}
0
Senseful

Une solution concise mais complète dans Swift 4.2 prend en compte les commandes UINavigationControllers , UITabBarControllers , présentées et enfants : voir les contrôleurs:

extension UIViewController {
  func topmostViewController() -> UIViewController {
    if let navigationVC = self as? UINavigationController,
      let topVC = navigationVC.topViewController {
      return topVC.topmostViewController()
    }
    if let tabBarVC = self as? UITabBarController,
      let selectedVC = tabBarVC.selectedViewController {
      return selectedVC.topmostViewController()
    }
    if let presentedVC = presentedViewController {
      return presentedVC.topmostViewController()
    }
    if let childVC = children.last {
      return childVC.topmostViewController()
    }
    return self
  }
}

extension UIApplication {
  func topmostViewController() -> UIViewController? {
    return keyWindow?.rootViewController?.topmostViewController()
  }
}

Usage:

let viewController = UIApplication.shared.topmostViewController()
0
NAlexN