Как найти самый верхний контроллер вида на iOS - PullRequest
236 голосов
/ 26 мая 2011

Я столкнулся с парой случаев, когда было бы удобно найти "самый верхний" контроллер представления (тот, который отвечает за текущее представление), но не нашел способа сделать это.

В основном проблема заключается в следующем: учитывая, что один выполняется в классе, который не является контроллером представления (или представление) [и не имеет адреса активного вида] и не был передан адрес самого верхнего контроллера вида (или, скажем, адрес контроллера навигации), возможно ли найти этот контроллер вида?(И если да, то как?)

Или, если это не удастся, можно ли найти самый верхний вид?

Ответы [ 40 ]

2 голосов
/ 28 ноября 2017

Я думаю, что большинство ответов полностью проигнорировано UINavigationViewController, поэтому я обработал этот вариант использования со следующей реализацией.

+ (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 голосов
/ 04 июля 2015

Отличное решение в Swift, внедрить в 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
}
1 голос
/ 19 декабря 2014

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)
        }
1 голос
/ 26 февраля 2014

Не уверен, поможет ли это тому, чего вы пытаетесь достичь, найдя самый верхний контроллер вида, но я пытался представить новый контроллер вида, но если бы у моего корневого контроллера вида уже был модальный диалог, он был бы заблокирован , поэтому я бы переместился на вершину всех контроллеров модального представления, используя этот код:

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

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}
1 голос
/ 01 мая 2019

Используйте расширение ниже, чтобы захватить текущий видимый UIViewController.Работал на Swift 4.0 и выше

Код:

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

Как использовать?

let objViewcontroller = UIApplication.topViewController()
1 голос
/ 30 июня 2016

И еще одно решение 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 голос
/ 22 октября 2017

Многие из этих ответов неполные. Хотя это в Objective-C, это лучшая компиляция из всех, которые я мог бы собрать прямо сейчас, как нерекурсивный блок:

UIViewController *(^topmostViewControllerForFrontmostNormalLevelWindow)(void) = ^UIViewController *{
    // NOTE: Adapted from various stray answers here:
    //   /5441350/kak-naiti-samyi-verhnii-kontroller-vida-na-ios

    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 голос
/ 16 октября 2015

Ниже две функции могут помочь найти topViewController в стеке контроллеров представления.Возможно, вам понадобится настройка позже, но для этого кода полезно понять концепцию topViewController или стека 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;
}

Вы можете использовать метод [viewController Class], чтобы узнать тип класса viewController.

0 голосов
/ 26 февраля 2014

Вы можете найти самый верхний контроллер вида, используя

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];
0 голосов
/ 11 сентября 2014

Вы должны использовать:

[UIApplication sharedApplication].window.rootViewController;

Когда на [UIApplication sharedApplication].keyWindow есть лист изменений, неправильно использовать keyWindow, как указано в в этом ответе .

...