我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。

基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)

或者,如果找不到,有没有可能找到最高处的风景?


当前回答

为了避免很多复杂性,我通过在委托中创建一个viewController来跟踪当前的viewController,并在每个viewDidLoad方法中设置它为self,这样每当你加载一个新视图时,委托中持有的viewController将对应于该视图的viewController。这可能是丑陋的,但它工作得很好,没有必要有一个导航控制器或任何废话。

其他回答

为了完成Eric的回答(谁省略了弹出窗口,导航控制器,选项卡控制器,视图控制器添加为子视图到一些其他的视图控制器遍历),这是我的版本返回当前可见的视图控制器:

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

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

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

现在你需要做的就是像下面这样调用上面的方法:

UIViewController *topMostViewControllerObj = [self topViewController];

如果根控制器是一个导航控制器,找到顶部可见控制器的正确方法是:

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

以下是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.

我知道已经很晚了,可能是多余的。但以下是我提出的对我有用的片段:

    static func topViewController() -> UIViewController? {
        return topViewController(vc: UIApplication.shared.keyWindow?.rootViewController)
    }

    private static func topViewController(vc:UIViewController?) -> UIViewController? {
        if let rootVC = vc {
            guard let presentedVC = rootVC.presentedViewController else {
                return rootVC
            }
            if let presentedNavVC = presentedVC as? UINavigationController {
                let lastVC = presentedNavVC.viewControllers.last
                return topViewController(vc: lastVC)
            }
            return topViewController(vc: presentedVC)
        }
        return nil
    }

以下是对我有效的方法。

我发现有时候控制器在键窗口上是nil,因为键窗口是一些操作系统的东西,如警报等。

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

另一个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)

}