【问题标题】:How to get visible viewController from app delegate when using storyboard?使用情节提要时如何从应用程序委托中获取可见的viewController?
【发布时间】:2020-12-08 23:45:30
【问题描述】:

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

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

【问题讨论】:

    标签: ios


    【解决方案1】:

    这应该为你做:

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

    【讨论】:

    • 当根视图控制器是导航控制器或标签栏控制器时...?
    • 也许你应该阅读他的问题:“而且我不使用导航控制器”
    • 不是说他不用标签栏控制器吗?此外,以模态方式呈现的观点又如何呢?这将返回根视图,它不会是模态呈现的视图。
    • 也许吧。但你的答案应该回答所提出的问题——它没有。
    • 感谢一百万次
    【解决方案2】:

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

    【讨论】:

    • 工作就像一个魅力!谢谢
    • 酷,请注意我将return rootViewController! 更改为return rootViewController,因为它可能为nil。
    • 如何查看currentViewController 的值?例如,如果我想说if currentViewController == someViewController { //do this } else { //do that} 有没有办法?打印变量会给出名称,但会附加一个在构建之间不维护的标识符。
    • @Craig.Pearce:您应该能够通过调用此函数然后针对您期望的特定类型测试返回值来做到这一点。比如:let currentViewController = getVisibleViewController(nil) if currentViewController.isKindOfClass(YourViewControllerClass) { ... } else ...
    • 不是rootViewController总是像here提到的那样固定,为什么需要作为参数传入?
    【解决方案3】:

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

    【讨论】:

    • 迄今为止最好和最干净的方法。
    • 我发现需要更改 tabbarcontroller 条件以检查 selectedViewController 是否是导航控制器,如果是,那么我得到导航控制器可见视图控制器。因为导航控制器不能/不应该包含 tabor 控制器,所以不需要反向检查。
    【解决方案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
      }
      

    【讨论】:

    • 你可以把它放在任何类/结构/枚举中,因为它是一个静态变量。例如,如果你把它放在AppDelegate 中,你会用AppDelegate.visibleViewController 来调用它。
    • 这是否处理嵌套在标签栏控制器内的导航控制器?
    • 是的,当导航控制器是标签栏控制器中选择的VC时应该处理。
    【解决方案5】:

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

    1) Cocoa Touch 的视图层次结构允许多个子级同时出现和可见,因此我们需要改为询问当前可见的视图控制器(复数)并相应地处理结果

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

    3) 我们可能希望忽略弹出模式和特定类型的视图控制器,例如 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 []
        }
    }
    

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        如果你使用的是 IQKeyboardManager,它们里面有一个扩展

        • (UIViewController*)currentViewController;

        这样你就可以了

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

        所以把它添加到你的 pod 文件中

        pod 'IQKeyboardManager'
        
        then pod update and you are away!
        

        希望对你有帮助

        【讨论】:

          【解决方案8】:

          这是@ProgrammierTier 答案的改进版本。如果您有一个嵌套在标签栏中的导航栏,您将使用@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
          }
          

          【讨论】:

            【解决方案9】:

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

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

            如果你使用的是 UITabBarController,那么你可以使用这个:

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

            【讨论】:

              【解决方案10】:

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

              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
              }
              

              【讨论】:

                【解决方案11】:

                这只是一个受@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
                    }
                }
                

                【讨论】:

                • 如何在应用委托类中使用这个
                • 不幸的是,UIApplication.shared.keyWindow 在 iOS 13.0 中已被弃用
                【解决方案12】:

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

                【讨论】:

                  【解决方案13】:

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

                   UIViewController *loginViewController = self.window.rootViewController;
                      
                   UITabBarController *controller = loginViewController.tabBarController;
                      
                   UIViewController *currentController = controller.selectedViewController.childViewControllers.lastObject;
                  

                  【讨论】:

                    【解决方案14】:

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2012-01-16
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2022-01-22
                      • 1970-01-01
                      相关资源
                      最近更新 更多