如何在使用storyboard时从app delegate获取可见的viewController?

use*_*341 30 ios

我有一些viewControllers,我不使用NavigationController.如何在app委托方法中获取可见的视图控制器(例如applicationWillResignActive)?

我知道怎么做NSNotification,但我认为这是错误的方式.

klc*_*r89 51

这应该为你做:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

  • 也许.但你的回答应该回答问题 - 事实并非如此. (2认同)

Pro*_*ier 37

@ aviatorken89的答案对我很有用.我不得不把它翻译成Swift - 对于任何从Swift开始的人:

针对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
}
Run Code Online (Sandbox Code Playgroud)

老答案:

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
}
Run Code Online (Sandbox Code Playgroud)


小智 9

我们将其实现为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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 迄今为止最好、最干净的方法。 (2认同)

kme*_*l96 7

这是Swift 4中的答案,它与已接受的答案非常相似,但有一些改进:

  1. 迭代而不是递归。
  2. 一直到导航堆栈。
  3. 更多“敏捷”语法。
  4. 可以放置在任何地方的静态变量(不仅限于AppDelegate)。
  5. 在奇怪的情况下(即当选项卡栏控制器没有选定的视图控制器时)不会崩溃。

    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
    }
    
    Run Code Online (Sandbox Code Playgroud)


小智 5

这里的顶级建议在许多情况下都可以正常工作,以获得“最佳猜测”解决方案,但通过一些细微的调整,我们可以获得不依赖于应用程序的视图层次结构实现的更完整的解决方案。

1) Cocoa Touch 的视图层次结构允许多个子视图同时存在和可见,因此我们需要请求当前可见视图控制器(复数)并相应地处理结果

2) UINavigationControllers 和UITabBarControllers 在 iOS 应用程序中常用,但它们并不是唯一一种容器视图控制器。UIKit 还提供UIPageViewController、 、UISplitViewController,并允许您编写自己的自定义容器视图控制器。

3)我们可能想忽略弹出框模式和特定类型的视图控制器,例如UIAlertController或自定义嵌入式子视图控制器。

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: $0) }) || vc.modalPresentationStyle == .popover
    }

    if let presented = parent.presentedViewController, !isExcluded(presented) {
        return self.visibleLeaves(from: presented, excluding: excludedTypes)
    }

    let visibleChildren = parent.childViewControllers.filter {
        $0.isViewLoaded && $0.view.window != nil
    }

    let visibleLeaves = visibleChildren.flatMap {
        return self.visibleLeaves(from: $0, excluding: excludedTypes)
    }

    if visibleLeaves.count > 0 {
        return visibleLeaves
    } else if !isExcluded(parent) {
        return [parent]
    } else {
        return []
    }
}
Run Code Online (Sandbox Code Playgroud)