如何在 iOS 上找到最顶层的视图控制器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6131205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to find topmost view controller on iOS
提问by Hot Licks
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 found a way to do it.
我现在遇到了几种情况,可以方便地找到“最顶层”视图控制器(负责当前视图的控制器),但还没有找到方法。
Basically the challenge is this: Given that one is executing in a class that is not a view controller(or a view) [and does not have the address of an active view]and has not been passed the address of the topmost view controller (or, say, the address of the navigation controller), is it possible to find that view controller? (And, if so, how?)
基本上挑战是这样的:假设一个人在一个不是视图控制器(或视图)的类中执行[并且没有活动视图的地址]并且没有传递最顶层视图控制器的地址(或者说,导航控制器的地址),是否可以找到该视图控制器?(而且,如果是这样,如何?)
Or, failing that, is it possible to find the topmost view?
或者,如果失败,是否有可能找到最顶层的视图?
采纳答案by Wilbur Vandrsmith
iOS 4 introduced the rootViewController property on UIWindow:
iOS 4 在 UIWindow 上引入了 rootViewController 属性:
[UIApplication sharedApplication].keyWindow.rootViewController;
You'll need to set it yourself after you create the view controller though.
但是,您需要在创建视图控制器后自己设置它。
回答by Eric
I think you need a combination of the accepted answer and @fishstix's
我认为您需要结合接受的答案和@fishstix
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
Swift 3.0+
斯威夫特 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
}
回答by kleo
To complete JonasG's answer(who left out tab bar controllers while traversing), here is my version of returning the currently visible view controller:
为了完成 JonasG 的回答(他在遍历时遗漏了标签栏控制器),这是我返回当前可见视图控制器的版本:
- (UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}
回答by Yuchen Zhong
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
- 视图控制器正在呈现另一个视图
- 视图控制器是一个
UINavigationController
- 视图控制器是一个
UITabBarController
Objective-C
目标-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+
斯威夫特 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
}
}
回答by Varuna
Getting top most view controller for Swift using extensions
使用扩展为 Swift 获取最顶级的视图控制器
Code:
代码:
extension UIViewController {
@objc func topMostViewController() -> UIViewController {
// Handling Modal views
if let presentedViewController = self.presentedViewController {
return presentedViewController.topMostViewController()
}
// Handling UIViewController's added as subviews to some other views.
else {
for view in self.view.subviews
{
// Key property which most of us are unaware of / rarely use.
if let subViewController = view.next {
if subViewController is UIViewController {
let viewController = subViewController as! UIViewController
return viewController.topMostViewController()
}
}
}
return self
}
}
}
extension UITabBarController {
override func topMostViewController() -> UIViewController {
return self.selectedViewController!.topMostViewController()
}
}
extension UINavigationController {
override func topMostViewController() -> UIViewController {
return self.visibleViewController!.topMostViewController()
}
}
Usage:
用法:
UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()
回答by Rajesh
To complete Eric's answer(who left out popovers, navigation controllers, tabbarcontrollers, view controllers added as subviews to some other view controllers while traversing), here is my version of returning the currently visible view controller:
为了完成 Eric 的回答(他省略了弹出框、导航控制器、tabbarcontrollers、在遍历时作为子视图添加到其他一些视图控制器的视图控制器),这是我返回当前可见视图控制器的版本:
=====================================================================
================================================== ====================
- (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;
}
}
=====================================================================
================================================== ====================
And now all you need to do to get top most view controller is call the above method as follows:
现在你需要做的就是获得最顶层的视图控制器,如下所示调用上面的方法:
UIViewController *topMostViewControllerObj = [self topViewController];
回答by Awesome-o
This answer includes childViewControllers
and maintains a clean and readable implementation.
这个答案包括childViewControllers
并维护了一个干净易读的实现。
+ (UIViewController *)topViewController
{
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
return [rootViewController topVisibleViewController];
}
- (UIViewController *)topVisibleViewController
{
if ([self isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)self;
return [tabBarController.selectedViewController topVisibleViewController];
}
else if ([self isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)self;
return [navigationController.visibleViewController topVisibleViewController];
}
else if (self.presentedViewController)
{
return [self.presentedViewController topVisibleViewController];
}
else if (self.childViewControllers.count > 0)
{
return [self.childViewControllers.lastObject topVisibleViewController];
}
return self;
}
回答by ipodishima
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.
我最近在我的一个项目中遇到了这种情况,当网络状态发生变化时,它需要显示一个通知视图,无论显示的控制器是什么,类型是什么(UINavigationController、经典控制器或自定义视图控制器)。
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
你可以在这里获取代码:PPTopMostController
And got the top most controller using
并使用了最顶级的控制器
UIViewController *c = [UIViewController topMostController];
回答by JonasG
This is an improvement to Eric's answer:
这是对 Eric 回答的改进:
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.
_topMostController(UIViewController *cont)
是一个辅助函数。
Now all you need to do is call topMostController()
and the top most UIViewController should be returned!
现在你需要做的就是调用topMostController()
并返回最顶层的 UIViewController !
回答by Kamran Khan
Here is my take on this. Thanks to @Stakenborg for pointing out the way to skip getting UIAlertView as the top most controller
这是我对此的看法。感谢@Stakenborg 指出跳过让 UIAlertView 作为最顶层控制器的方法
-(UIWindow *) returnWindowWithWindowLevelNormal
{
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *topWindow in windows)
{
if (topWindow.windowLevel == UIWindowLevelNormal)
return topWindow;
}
return [UIApplication sharedApplication].keyWindow;
}
-(UIViewController *) getTopMostController
{
UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
if (topWindow.windowLevel != UIWindowLevelNormal)
{
topWindow = [self returnWindowWithWindowLevelNormal];
}
UIViewController *topController = topWindow.rootViewController;
if(topController == nil)
{
topWindow = [UIApplication sharedApplication].delegate.window;
if (topWindow.windowLevel != UIWindowLevelNormal)
{
topWindow = [self returnWindowWithWindowLevelNormal];
}
topController = topWindow.rootViewController;
}
while(topController.presentedViewController)
{
topController = topController.presentedViewController;
}
if([topController isKindOfClass:[UINavigationController class]])
{
UINavigationController *nav = (UINavigationController*)topController;
topController = [nav.viewControllers lastObject];
while(topController.presentedViewController)
{
topController = topController.presentedViewController;
}
}
return topController;
}