ios 使用故事板时如何从应用程序委托中获取可见的viewController?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22882078/
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 get visible viewController from app delegate when using storyboard?
提问by user3501341
I have some viewControllers
, and I don't use NavigationController
.
How can I get visible view controller in app delegate methods (e.g. applicationWillResignActive
)?
我有一些viewControllers
,我不使用NavigationController
。如何在应用程序委托方法(例如applicationWillResignActive
)中获得可见的视图控制器?
I know how to do it from NSNotification
, but I think it's the wrong way.
我知道怎么做NSNotification
,但我认为这是错误的方式。
回答by klcjr89
This should do it for you:
这应该为你做:
- (void)applicationWillResignActive:(UIApplication *)application
{
UIViewController *vc = [self visibleViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil)
{
return rootViewController;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self visibleViewController:lastViewController];
}
if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [self visibleViewController:selectedViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self visibleViewController:presentedViewController];
}
回答by ProgrammierTier
@aviatorken89's answer worked well for me. I had to translate it to Swift - for anybody starting out with Swift:
@ aviatorken89 的回答对我很有效。我不得不将它翻译成 Swift - 对于任何开始使用 Swift 的人:
Updated for Swift 3:
为 Swift 3 更新:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
Old answer:
旧答案:
func applicationWillResignActive(application: UIApplication) {
let currentViewController = getVisibleViewController(nil)
}
func getVisibleViewController(var rootViewController: UIViewController?) -> UIViewController? {
if rootViewController == nil {
rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
}
if rootViewController?.presentedViewController == nil {
return rootViewController
}
if let presented = rootViewController?.presentedViewController {
if presented.isKindOfClass(UINavigationController) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKindOfClass(UITabBarController) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
回答by crelies
We implemented it as an UIApplication extension:
我们将其实现为 UIApplication 扩展:
import UIKit
extension UIApplication {
var visibleViewController: UIViewController? {
guard let rootViewController = keyWindow?.rootViewController else {
return nil
}
return getVisibleViewController(rootViewController)
}
private func getVisibleViewController(_ rootViewController: UIViewController) -> UIViewController? {
if let presentedViewController = rootViewController.presentedViewController {
return getVisibleViewController(presentedViewController)
}
if let navigationController = rootViewController as? UINavigationController {
return navigationController.visibleViewController
}
if let tabBarController = rootViewController as? UITabBarController {
return tabBarController.selectedViewController
}
return rootViewController
}
}
回答by kmell96
Here is an answer in Swift 4 that is very similar to the accepted answer but has a few improvements:
这是 Swift 4 中的一个答案,它与公认的答案非常相似,但有一些改进:
- Iterative instead of recursive.
- Goes all the way does the navigation stack.
- More "swifty" syntax.
- Static variable that can be put anywhere (not just in AppDelegate).
Won't crash in odd cases, i.e. when a tab bar controller has no selected view controller.
static var visibleViewController: UIViewController? { var currentVc = UIApplication.shared.keyWindow?.rootViewController while let presentedVc = currentVc?.presentedViewController { if let navVc = (presentedVc as? UINavigationController)?.viewControllers.last { currentVc = navVc } else if let tabVc = (presentedVc as? UITabBarController)?.selectedViewController { currentVc = tabVc } else { currentVc = presentedVc } } return currentVc }
- 迭代而不是递归。
- 一路导航堆栈。
- 更“快速”的语法。
- 可以放在任何地方的静态变量(不仅仅是在 AppDelegate 中)。
在奇怪的情况下不会崩溃,即当标签栏控制器没有选择的视图控制器时。
static var visibleViewController: UIViewController? { var currentVc = UIApplication.shared.keyWindow?.rootViewController while let presentedVc = currentVc?.presentedViewController { if let navVc = (presentedVc as? UINavigationController)?.viewControllers.last { currentVc = navVc } else if let tabVc = (presentedVc as? UITabBarController)?.selectedViewController { currentVc = tabVc } else { currentVc = presentedVc } } return currentVc }
回答by upsidejames
The top recommendations here will work ok in many scenarios to get the 'best guess'solution but with a few minor adjustments, we can get a more complete solution that doesn't rely on your app's view hierarchy implementation.
这里的顶级建议在许多情况下都可以正常工作以获得“最佳猜测”解决方案,但通过一些小的调整,我们可以获得更完整的解决方案,不依赖于您的应用程序的视图层次结构实现。
1) Cocoa Touch's view hierarchy allows for multiple children to be present and visible at one time so we need to instead ask for the current visible view controllers (plural) and handle the results accordingly
1) Cocoa Touch 的视图层次结构允许多个子项同时存在和可见,因此我们需要请求当前可见的视图控制器(复数)并相应地处理结果
2) UINavigationController
s and UITabBarController
s are commonly used in iOS applications, but they are not the only kind of container view controllers. UIKit also supplies the UIPageViewController
, UISplitViewController
, and allows you to write your own custom container view controllers.
2) UINavigationController
s 和UITabBarController
s 在 iOS 应用中很常用,但它们并不是唯一的容器视图控制器。UIKit 还提供UIPageViewController
, UISplitViewController
, 并允许您编写自己的自定义容器视图控制器。
3) We probably want to ignore popover modals and specific types of view controllers such UIAlertController
s or a custom embedded child-viewcontroller.
3)我们可能想忽略弹出窗口模式和特定类型的视图控制器,例如UIAlertController
s 或自定义嵌入式子视图控制器。
private func visibleViewControllers() -> [UIViewController] {
guard let root = window?.rootViewController else { return [] }
return visibleLeaves(from: root, excluding: [UIAlertController.self])
}
private func visibleLeaves(from parent: UIViewController, excluding excludedTypes: [UIViewController.Type] = []) -> [UIViewController] {
let isExcluded: (UIViewController) -> Bool = { vc in
excludedTypes.contains(where: { vc.isKind(of: public protocol ViewControllerContainer {
var topMostViewController: UIViewController? { get }
}
extension UIViewController: ViewControllerContainer {
public var topMostViewController: UIViewController? {
if let presentedView = presentedViewController {
return recurseViewController(presentedView)
}
return childViewControllers.last.map(recurseViewController)
}
}
extension UITabBarController {
public override var topMostViewController: UIViewController? {
return selectedViewController.map(recurseViewController)
}
}
extension UINavigationController {
public override var topMostViewController: UIViewController? {
return viewControllers.last.map(recurseViewController)
}
}
extension UIWindow: ViewControllerContainer {
public var topMostViewController: UIViewController? {
return rootViewController.map(recurseViewController)
}
}
func recurseViewController(viewController: UIViewController) -> UIViewController {
return viewController.topMostViewController.map(recurseViewController) ?? viewController
}
) }) || vc.modalPresentationStyle == .popover
}
if let presented = parent.presentedViewController, !isExcluded(presented) {
return self.visibleLeaves(from: presented, excluding: excludedTypes)
}
let visibleChildren = parent.childViewControllers.filter {
UIViewController *currentControllerName = ((UINavigationController*)appDelegate.window.rootViewController).visibleViewController;
.isViewLoaded && UIViewController *currentControllerName = ((UITabBarController*)appDelegate.window.rootViewController).selectedViewController;
.view.window != nil
}
let visibleLeaves = visibleChildren.flatMap {
return self.visibleLeaves(from: application.keyWindow?.currentViewController? // <- there you go
, excluding: excludedTypes)
}
if visibleLeaves.count > 0 {
return visibleLeaves
} else if !isExcluded(parent) {
return [parent]
} else {
return []
}
}
回答by Patrick Goley
Here is a recursive, protocol-oriented approach in Swift. Can be extended to custom types but any kind of UIViewController subclass should work with the code below.
这是 Swift 中递归的、面向协议的方法。可以扩展到自定义类型,但任何类型的 UIViewController 子类都应该使用下面的代码。
pod 'IQKeyboardManager'
then pod update and you are away!
回答by Rizwan Shah
If your app's root view controller is a UINavigationController than you can use this:
如果您的应用程序的根视图控制器是 UINavigationController,那么您可以使用它:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
var presented = rootVC?.presentedViewController
if rootVC?.presentedViewController == nil {
if let isTab = rootVC?.isKind(of: UITabBarController.self), let isNav = rootVC?.isKind(of: UINavigationController.self) {
if !isTab && !isNav {
return rootVC
}
presented = rootVC
} else {
return rootVC
}
}
if let presented = presented {
if presented.isKind(of: UINavigationController.self) {
if let navigationController = presented as? UINavigationController {
return navigationController.viewControllers.last!
}
}
if presented.isKind(of: UITabBarController.self) {
if let tabBarController = presented as? UITabBarController {
if let navigationController = tabBarController.selectedViewController! as? UINavigationController {
return navigationController.viewControllers.last!
} else {
return tabBarController.selectedViewController!
}
}
}
return getVisibleViewController(presented)
}
return nil
}
and if you are using UITabBarController than you can use this:
如果您使用的是 UITabBarController,则可以使用以下命令:
extension UIApplication {
var visibleViewController: UIViewController? {
return getVisibleViewController(nil)
}
private func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
let rootVC = rootViewController ?? UIApplication.shared.keyWindow?.rootViewController
if rootVC!.isKind(of: UINavigationController.self) {
let navigationController = rootVC as! UINavigationController
return getVisibleViewController(navigationController.viewControllers.last!)
}
if rootVC!.isKind(of: UITabBarController.self) {
let tabBarController = rootVC as! UITabBarController
return getVisibleViewController(tabBarController.selectedViewController!)
}
if let presentedVC = rootVC?.presentedViewController {
return getVisibleViewController(presentedVC)
}
return rootVC
}
}
回答by Mark Gilchrist
If you are using IQKeyboardManager they have an extension in there
如果您使用的是 IQKeyboardManager,它们在那里有一个扩展
- (UIViewController*)currentViewController;
- (UIViewController*)currentViewController;
so you can do
所以你可以做
##代码##so add this to your pod file
所以把它添加到你的 pod 文件中
##代码##hope this helps
希望这可以帮助
回答by pyromancer2
This is an improved version of @ProgrammierTier's answer. If you have a navbar nested in a tabbar, you will get back UINavigationController using @ProgrammierTier's answer. Also, there is less force unwrapping. This should address the issue @Harendra-Tiwari is facing.
这是@ProgrammierTier 答案的改进版本。如果导航栏嵌套在选项卡栏中,您将使用@ProgrammierTier 的答案返回 UINavigationController。此外,展开的力较小。这应该解决@Harendra-Tiwari 面临的问题。
Swift 4.2:
斯威夫特 4.2:
##代码##回答by Ambroise Collon
Here's just a quick fix inspired from @krcjr89's answer. The accepted answer doesn't go all the way down the navigation. For instance, if you have a navigation controller embedded in tab bar controller, you won't get to the visible view controller but the navigation controller.
这只是受@krcjr89 回答启发的快速修复。接受的答案并没有在导航中一路向下。例如,如果你有一个嵌入在标签栏控制器中的导航控制器,你将不会到达可见视图控制器而是导航控制器。
I made it an extension of UIApplication like @Christian, as this makes the most sense.
我把它变成了像@Christian 这样的 UIApplication 的扩展,因为这是最有意义的。
##代码##