如何在 iOS 上找到最顶层的视图控制器
我现在遇到了几种情况,能够很方便地找到“最顶层”视图控制器(负责当前视图的控制器),但还没有找到一种方法来做到这一点。
基本上,挑战是这样的:假设一个在不是视图控制器的类中执行(或视图)[并且没有活动视图的地址]并且尚未传递最顶层视图控制器的地址(或者说,导航控制器的地址),是否可以找到该视图控制器? (如果是的话,怎么办?)
或者,如果做不到这一点,是否有可能找到最顶层的视图?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
我认为你需要结合接受的答案和 @fishstix 的
Swift 3.0+
I think you need a combination of the accepted answer and @fishstix's
Swift 3.0+
为了完成 JonasG 的答案(他在遍历时遗漏了标签栏控制器),这是我返回当前可见视图控制器的版本:
To complete JonasG's answer (who left out tab bar controllers while traversing), here is my version of returning the currently visible view controller:
iOS 4 在 UIWindow 上引入了 rootViewController 属性:
不过,您需要在创建视图控制器后自行设置它。
iOS 4 introduced the rootViewController property on UIWindow:
You'll need to set it yourself after you create the view controller though.
一个完整的非递归版本,照顾不同的场景:
UINavigationController
UITabBarController
Objective -C
Swift 4+
A complete non-recursive version, taking care of different scenarios:
UINavigationController
UITabBarController
Objective-C
Swift 4+
使用扩展获取 Swift 的最顶层视图控制器
代码:
用法:
Getting top most view controller for Swift using extensions
Code:
Usage:
完成埃里克的答案(他遗漏了弹出窗口、导航控制器、选项卡控制器、作为子视图添加到其他一些视图控制器的视图控制器,而遍历),这是我返回当前可见视图控制器的版本:
=================================================== ===================
================================= ========================================
现在您需要做的就是获得最上面的视图控制器调用上述方法如下:
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:
=====================================================================
=====================================================================
And now all you need to do to get top most view controller is call the above method as follows:
这个答案包括
childViewControllers
并保持干净且可读的实现。This answer includes
childViewControllers
and maintains a clean and readable implementation.我最近在我的一个项目中遇到了这种情况,当网络状态发生变化时,该项目需要显示一个通知视图,无论控制器显示的是什么以及类型(UINavigationController、经典控制器或自定义视图控制器)。
所以我刚刚发布了我的代码,它非常简单并且实际上基于协议,因此它对于每种类型的容器控制器都很灵活。
它似乎与最后的答案相关,但以一种非常灵活的方式。
您可以在此处获取代码: PPTopMostController
并使用
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
对于最新的 Swift 版本:
创建一个文件,将其命名为
UIWindowExtension.swift
并粘贴以下代码片段:在任何地方使用它:
For latest Swift Version:
Create a file, name it
UIWindowExtension.swift
and paste the following snippet:Use it anywhere as:
这是对 Eric 答案的改进:
_topMostController(UIViewController *cont)
是一个辅助函数。现在您需要做的就是调用
topMostController()
并且应该返回最顶层的 UIViewController!This is an improvement to Eric's answer:
_topMostController(UIViewController *cont)
is a helper function.Now all you need to do is call
topMostController()
and the top most UIViewController should be returned!使用下面的扩展来获取当前可见的
UIViewController
。适用于 Swift 4.0 及更高版本Swift 4.0 及更高版本:
如何使用?
Use below extension to grab current visible
UIViewController
. Worked for Swift 4.0 and laterSwift 4.0 and Later:
How to use?
这是我对此的看法。感谢@Stakenborg 指出了跳过将 UIAlertView 作为最顶层控制器的方法
Here is my take on this. Thanks to @Stakenborg for pointing out the way to skip getting UIAlertView as the top most controller
Swift 中
UIApplication
的简单扩展:注意:
它关心
UITabBarController
中的moreNavigationController
简单用法:
Simple extension for
UIApplication
in Swift:NOTE:
It cares about
moreNavigationController
withinUITabBarController
Simple usage:
Swift 4.2 扩展
可以从任何地方使用它,
或者适合
任何类,如 UINavigationController、UITabBarController
享受吧!
Swift 4.2 Extension
Use it from anywhere like,
or like,
Fit to any classes like UINavigationController, UITabBarController
Enjoy!
Swift 4.2 中简洁而全面的解决方案考虑了 UINavigationControllers、UITabBarControllers、presented 和 child 视图控制器:
用法:
A concise yet comprehensive solution in Swift 4.2, takes into account UINavigationControllers, UITabBarControllers, presented and child view controllers:
Usage:
另一个 Swift 解决方案
Yet another Swift solution
这对我有用。
我发现有时控制器在关键窗口上为零,因为关键窗口是一些操作系统的东西,比如警报等。
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.
扩展@Eric的答案,你需要小心 keyWindow 实际上是你想要的窗口。例如,如果您在点击警报视图中的某些内容后尝试使用此方法,则 keyWindow 实际上将是警报的窗口,这无疑会给您带来问题。当我通过警报处理深层链接时,这种情况发生在我身上,并导致 SIGABRT 没有堆栈跟踪。总要调试。
这是我现在使用的代码:
随意将其与从该问题的其他答案中检索您喜欢的顶视图控制器的任何风格混合。
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:
Feel free to mix this with whatever flavor of retrieving the top view controller you like from the other answers on this question.
替代 Swift 解决方案:
Alternative Swift solution:
这个解决方案是最完整的。它考虑到:
UI导航控制器
UIPageViewController
UITabBar控制器
顶部视图控制器中最顶部呈现的视图控制器
示例位于 Swift 3 中。
有 3 个重载
This solution is the most complete. It takes in consideration:
UINavigationController
UIPageViewController
UITabBarController
And the topmost presented view controller from the top view controller
The example is in Swift 3.
There are 3 overloads
其中很多答案都是不完整的。虽然这是用 Objective-C 编写的,但这是我现在可以将它们放在一起的最佳编译,作为一个非递归块:
链接到要点,以防万一修改: https://gist.github.com/benguild/0d149bb3caaabea2dac3d2dca58c0816
代码供参考/比较:
A lot of these answers are incomplete. Although this is in Objective-C, this is the best compilation of all of them that I could put together for right now, as a non-recursive block:
Link to Gist, in case it gets revised: https://gist.github.com/benguild/0d149bb3caaabea2dac3d2dca58c0816
Code for reference/comparison:
Swift 中的出色解决方案,在 AppDelegate 中实现
Great solution in Swift, implement in AppDelegate
我知道已经很晚了而且可能是多余的。但以下是我想出的对我有用的片段:
I know its very late and might be redundant. But following is the snippet I came up with which is working for me :
斯威夫特:
用法:
Swift:
Usage:
之前的答案似乎不能处理 rootController 是 UITabBarController 或 UINavigationController 的情况。
这是 swift 中适用于这些情况的函数:
Previous answer does not seems to handle cases where rootController are UITabBarController or UINavigationController.
Here is the function in swift which works for those cases :
我认为大多数答案都完全忽略了 UINavigationViewController,因此我通过以下实现来处理这个用例。
I think most of the answers have completely ignored
UINavigationViewController
, so I handled this use case with following implementation.这对于查找顶部 viewController 1 从任何根视图控件
This works great for finding the top viewController 1 from any root view controlle
不确定这是否会帮助您通过查找最顶层的视图控制器来实现目标,但我试图呈现一个新的视图控制器,但如果我的根视图控制器已经有一个模式对话框,它将被阻止,所以我将使用以下代码循环到所有模态视图控制器的顶部:
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: