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

问题描述 投票:26回答:14

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

我知道如何从NSNotification做到这一点,但我认为这是错误的方式。

ios
14个回答
49
投票

这应该为你做:

- (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];
}

0
投票

这是@ ProgrammierTier作为UIViewController扩展的答案的Swift 2.3实现

extension UIViewController {
    var visibleViewController: UIViewController? {
        if presentedViewController == nil {
            return self
        }

        if let presented = 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 presented.visibleViewController
        }

        return nil
    }
}

applicationWillResignActive得到它

func applicationWillResignActive(application: UIApplication) {
    let visibleVC = application.keyWindow?.rootViewController?.visibleViewController
}

0
投票

在我的情况下,我有Tabbar控制器,然后导航控制器为每个Tab希望它帮助某人

 UIViewController *loginViewController=self.window.rootViewController;

 UITabBarController *controller=loginViewController.tabBarController;

 UIViewController *CurrentController = controller.selectedViewController.childViewControllers.lastObject;

0
投票

这里只是一个来自@ krcjr89答案的快速修复。接受的答案不会一直沿着导航。例如,如果您在标签栏控制器中嵌入了导航控制器,则无法访问可见视图控制器而是导航控制器。

我把它作为@Christian的UIApplication的扩展,因为这是最有意义的。

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
    }
}

0
投票

使用ObjC中的UIViewController类别的先前答案的修改版本:

的UIViewController + VisibleViewController.h

#import <UIKit/UIKit.h>

@interface UIViewController (VisibleViewController)

- (UIViewController *)visibleViewController;

@end

的UIViewController + VisibleViewController.m

#import "UIViewController+VisibleViewController.h"

@implementation UIViewController (VisibleViewController)

- (UIViewController *)visibleViewController {
    if (self.presentedViewController == nil) {
        return self;
    }
    if ([self.presentedViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
        UIViewController *lastViewController = [[navigationController viewControllers] lastObject];

        return [lastViewController visibleViewController];
    }
    if ([self.presentedViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController *tabBarController = (UITabBarController *)self.presentedViewController;
        UIViewController *selectedViewController = tabBarController.selectedViewController;

        return [selectedViewController visibleViewController];
    }

    UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;

    return [presentedViewController visibleViewController];
}

@end

AppDelegate.m

#import "UIViewController+VisibleViewController.h"

- (UIViewController *) applicationVisibleViewController {
    return [self.window.rootViewController visibleViewController];
}

-1
投票

从troop231修改

+ (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)rootViewController;
        UIViewController *lastViewController = [[navigationController viewControllers] lastObject];

        return [self visibleViewController:lastViewController];
    }
    if ([rootViewController isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)rootViewController;
        UIViewController *selectedViewController = tabBarController.selectedViewController;

        return [self visibleViewController:selectedViewController];
    }

    if (rootViewController.presentedViewController != nil)
    {
        UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
        return [self visibleViewController:presentedViewController];
    }

    return rootViewController;
}

34
投票

@ 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
}

老答案:

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
}

8
投票

我们将其实现为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
    }
}

4
投票

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

  1. 迭代而不是递归。
  2. 一路走到导航堆栈。
  3. 更“swifty”的语法。
  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 }

3
投票

这是Swift中一种递归的,面向协议的方法。可以扩展为自定义类型,但任何类型的UIViewController子类都应该使用下面的代码。

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
}

3
投票

这里的最佳建议在许多情况下都能正常工作以获得“最佳猜测”解决方案,但只需稍作调整,我们就可以获得更完整的解决方案,而不依赖于应用程序的视图层次结构实现。

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

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

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

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 []
    }
}

2
投票

如果您的应用程序的根视图控制器是UINavigationController,那么您可以使用:

UIViewController *currentControllerName = ((UINavigationController*)appDelegate.window.rootViewController).visibleViewController;

如果你使用的是UITabBarController,你可以使用:

UIViewController *currentControllerName = ((UITabBarController*)appDelegate.window.rootViewController).selectedViewController;

2
投票

如果您使用的是IQKeyboardManager,那么它们就有一个扩展名

  • (的UIViewController *)currentViewController;

所以你可以做到

 application.keyWindow?.currentViewController? // <- there you go

所以将此添加到您的pod文件中

pod 'IQKeyboardManager'

then pod update and you are away!

希望这可以帮助


1
投票

这是@ ProgrammierTier答案的改进版本。如果你有一个嵌套在tabbar中的导航栏,你将使用@ ProgrammierTier的答案返回UINavigationController。此外,力量展开的次数也较少。这应该解决@ Harendra-Tiwari面临的问题。

Swift 4.2:

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
}
© www.soinside.com 2019 - 2024. All rights reserved.