How to find topmost view controller on iOS

后端 未结 30 2310
遥遥无期
遥遥无期 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 09:03

    Yet another Swift solution

    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)
    
    }
    
    0 讨论(0)
  • 2020-11-22 09:03

    I think most of the answers have completely ignored UINavigationViewController, so I handled this use case with following implementation.

    + (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;
    }
    
    0 讨论(0)
  • 2020-11-22 09:04

    Here is what worked for me.

    I found that sometimes the controller was nil on the key window, as the keyWindow is some OS thing like an alert, 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;
     }
    
    0 讨论(0)
  • 2020-11-22 09:06

    Expanding on @Eric's answer, you need to be careful that the keyWindow is actually the window you want. If you are trying to utilize this method after tapping something in an alert view for example, the keyWindow will actually be the alert's window, and that will cause problems for you no doubt. This happened to me in the wild when handling deep links via an alert and caused SIGABRTs with NO STACK TRACE. Total bitch to debug.

    Here's the code I'm using now:

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

    Feel free to mix this with whatever flavor of retrieving the top view controller you like from the other answers on this question.

    0 讨论(0)
  • 2020-11-22 09:07

    Not sure if this will help what you're trying to accomplish by finding the topmost view controller, but I was trying to present a new view controller, but if my root view controller already had a modal dialog, it would be blocked, so I would cycle to the top of all modal view controllers using this code:

    UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;
    
    while( parentController.presentedViewController &&
           parentController != parentController.presentedViewController )
    {
        parentController = parentController.presentedViewController;
    }
    
    0 讨论(0)
  • 2020-11-22 09:08

    Use below extension to grab current visible UIViewController. Worked for Swift 4.0 and later

    Swift 4.0 and Later:

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

    How to use?

    let objViewcontroller = UIApplication.topViewController()
    
    0 讨论(0)
提交回复
热议问题