How to find topmost view controller on iOS

后端 未结 30 2305
遥遥无期
遥遥无期 2020-11-22 08:40

I\'ve run into a couple of cases now where it would be convenient to be able to find the \"topmost\" view controller (the one responsible for the current view), but haven\'t

相关标签:
30条回答
  • 2020-11-22 08:54

    I recently got this situation in one my project, which required to displayed a notification view whatever the controller displayed was and whatever was the type (UINavigationController, classic controller or custom view controller), when network status changed.

    So I juste released my code, which is quite easy and actually based on a protocol so that it is flexible with every type of container controller. It seems to be related with the last answers, but in a much flexible way.

    You can grab the code here : PPTopMostController

    And got the top most controller using

    UIViewController *c = [UIViewController topMostController];
    
    0 讨论(0)
  • 2020-11-22 08:55

    A complete non-recursive version, taking care of different scenarios:

    • The view controller is presenting another view
    • The view controller is a UINavigationController
    • The view controller is a UITabBarController

    Objective-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
        }
    }
    
    0 讨论(0)
  • 2020-11-22 08:55

    A concise yet comprehensive solution in Swift 4.2, takes into account UINavigationControllers, UITabBarControllers, presented and child view controllers:

    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 讨论(0)
  • 2020-11-22 08:57

    This is an improvement to Eric's answer:

    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) is a helper function.

    Now all you need to do is call topMostController() and the top most UIViewController should be returned!

    0 讨论(0)
  • 2020-11-22 08:57

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

    Use it from anywhere like,

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

    or like,

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

    Fit to any classes like UINavigationController, UITabBarController

    Enjoy!

    0 讨论(0)
  • 2020-11-22 08:57

    Great solution in Swift, implement in 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
    }
    
    0 讨论(0)
提交回复
热议问题