我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。
基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)
或者,如果找不到,有没有可能找到最高处的风景?
我遇到了一些情况,现在,它将是方便的,能够找到“最顶层”的视图控制器(一个负责当前视图),但还没有找到一种方法。
基本上,挑战是这样的:给定一个在一个类中执行,这个类不是一个视图控制器(或一个视图)[并且没有活动视图的地址],并且没有传递最顶层视图控制器的地址(或者,比如说,导航控制器的地址),是否有可能找到那个视图控制器?(如果是的话,是怎么做到的?)
或者,如果找不到,有没有可能找到最高处的风景?
当前回答
很多答案都是不完整的。虽然这是在Objective-C中,但这是我现在能把它们放在一起的最好的编译,作为一个非递归的块:
链接到Gist,以防修改:https://gist.github.com/benguild/0d149bb3caaabea2dac3d2dca58c0816 供参考/比较的代码:
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;
};
其他回答
你应该使用:
[UIApplication sharedApplication].window.rootViewController;
当[UIApplication sharedApplication]上有一个uiactionsheet时。keyWindow,这是不正确的使用keyWindow在这个答案中提到。
我最近在我的一个项目中遇到了这种情况,当网络状态发生变化时,需要显示一个通知视图,无论显示的控制器是什么,无论类型是什么(UINavigationController,经典控制器或自定义视图控制器)。
所以我刚刚发布了我的代码,这很简单,实际上是基于一个协议,所以它对每种类型的容器控制器都是灵活的。 它似乎与上一个答案有关,但方式更加灵活。
你可以从这里获取代码:PPTopMostController
并得到最顶级的控制器使用
UIViewController *c = [UIViewController topMostController];
你可以通过使用找到最顶层视图控制器
NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];
我认为你需要一个公认的答案和@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
}
I am thinking that perhaps one thing is being overlooked here. Perhaps it is better to pass the parent viewController into the function that is using the viewController. If you are fishing around in the view hierarchy to find the top view controller that it is probably violating separation of the Model layer and UI layer and is a code smell. Just pointing this out, I did the same, then realized it was much simpler just to pass it in to function, by having the model operation return to the UI layer where I have a reference to the view controller.