【问题标题】:Get the current displaying UIViewController on the screen in AppDelegate.m在 AppDelegate.m 中获取当前屏幕上显示的 UIViewController
【发布时间】:2012-07-23 04:12:04
【问题描述】:

当前屏幕上的UIViewController 需要通过设置一些徽章视图来响应来自 APN 的推送通知。但是我怎样才能得到UIViewController in methodapplication:didReceiveRemoteNotification: of AppDelegate.m 中的UIViewController

我尝试使用self.window.rootViewController 来获取当前显示的UIViewController,它可能是UINavigationViewController 或其他类型的视图控制器。我发现UINavigationViewControllervisibleViewController 属性可用于在屏幕上获取UIViewController。但是如果不是UINavigationViewController,我该怎么办?

感谢任何帮助!相关代码如下。

AppDelegate.m

...
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    //I would like to find out which view controller is on the screen here.

    UIViewController *vc = [(UINavigationViewController *)self.window.rootViewController visibleViewController];
    [vc performSelector:@selector(handleThePushNotification:) withObject:userInfo];
}
...

ViewControllerA.m

- (void)handleThePushNotification:(NSDictionary *)userInfo{

    //set some badge view here

}

【问题讨论】:

    标签: iphone ios uiviewcontroller push-notification


    【解决方案1】:

    当您的控制器不是UINavigationController 时,您也可以使用rootViewController

    UIViewController *vc = self.window.rootViewController;
    

    一旦您了解了根视图控制器,那么这取决于您如何构建 UI,但您可能会找到一种在控制器层次结构中导航的方法。

    如果您提供有关您定义应用的方式的更多详细信息,那么我可能会提供更多提示。

    编辑:

    如果你想要最顶层的视图(不是视图控制器),你可以检查

    [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
    

    虽然这个视图可能是不可见的,甚至被它的一些子视图覆盖...

    同样,这取决于您的用户界面,但这可能会有所帮助...

    【讨论】:

    • 这个问题是如果可见视图不属于根视图控制器(在模态视图等的情况下)。
    • 是的,我愿意。但它可能是一个 UITabViewController。难道没有直接的方法可以让 UIViewController 出现在屏幕上吗?
    • 嗯,你看,UINavigationController 提供了一种方法让你知道哪个控制器是最顶层的;您的根控制器应该以某种方式提供相同的信息。一般不能推断它,因为它严格取决于你如何构建你的 UI,并且没有明确的控制器层次结构(就像它发生在视图中一样)。您可以简单地将一个属性添加到您的根控制器,并在您将新控制器“推”到顶部时设置其值。
    • 只要价值保持最新,这对我来说似乎也是一个好方法。
    • 没有从UIView 实例直接访问控制器的方法。 rootViewController不一定是当前显示的控制器。它位于视图层次结构的顶部。
    【解决方案2】:

    为什么不在应用委托中处理推送通知代码?它与视图直接相关吗?

    您可以通过检查视图的window 属性是否有值来检查UIViewController 的视图当前是否可见。查看更多here

    【讨论】:

    • 是的,它与视图有关,因为我必须显示徽章视图。让我检查一下链接。谢谢你:)
    【解决方案3】:

    您也可以通过 NSNotificationCenter 发布通知。这让您可以处理许多遍历视图控制器层次结构可能很棘手的情况 - 例如,当模态显示时等。

    例如,

    // MyAppDelegate.h
    NSString * const UIApplicationDidReceiveRemoteNotification;
    
    // MyAppDelegate.m
    NSString * const UIApplicationDidReceiveRemoteNotification = @"UIApplicationDidReceiveRemoteNotification";
    
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
        [[NSNotificationCenter defaultCenter]
         postNotificationName:UIApplicationDidReceiveRemoteNotification
         object:self
         userInfo:userInfo];
    }
    

    在您的每个视图控制器中:

    -(void)viewDidLoad {
        [[NSNotificationCenter defaultCenter] 
          addObserver:self
          selector:@selector(didReceiveRemoteNotification:)                                                  
          name:UIApplicationDidReceiveRemoteNotification
          object:nil];
    }
    
    -(void)viewDidUnload {
        [[NSNotificationCenter defaultCenter] 
          removeObserver:self
          name:UIApplicationDidReceiveRemoteNotification
          object:nil];
    }
    
    -(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
        // see http://stackoverflow.com/a/2777460/305149
       if (self.isViewLoaded && self.view.window) {
          // handle the notification
       }
    }
    

    您还可以使用这种方法来检测需要在收到通知时更新并由多个视图控制器使用的控件。在这种情况下,分别在 init 和 dealloc 方法中处理添加/删除观察者调用。

    【讨论】:

    • viewDidLoad 中的addObserver:bar 是什么?我必须用self替换吗?
    • 感谢您指出这一点 - 它应该是自我。我会更新答案。
    • 从 userInfo 获取所有密钥时崩溃。有什么想法吗? [NSConcreteNotification allKeys]:无法识别的选择器发送到实例 0x1fd87480 2013-07-05 16:10:36.469 Providence[2961:907] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:'-[NSConcreteNotification allKeys]:无法识别选择器发送到实例 0x1fd87480'
    • @AwaisTariq - 嗯 - 我的猜测是 iOS 传递给 didReceiveRemoteNotification 的对象实际上不是一个 NSDictionary,正如接口所指定的那样。
    • 如果用户还没有导航到你的观察者类怎么办? ://
    【解决方案4】:

    为每个ViewController指定标题,然后通过下面给出的代码获取当前ViewController的标题。

    -(void)viewDidUnload {
      NSString *currentController = self.navigationController.visibleViewController.title;
    

    然后像这样通过你的标题检查它

      if([currentController isEqualToString:@"myViewControllerTitle"]){
        //write your code according to View controller.
      }
    }
    

    【讨论】:

    • 绝对是最好的答案,您也可以将您的 viewController 命名为:self.title = myPhotoView
    【解决方案5】:

    关于上面的 NSNotificationCenter 帖子(抱歉找不到在哪里发表评论...)

    万一有些人收到 -[NSConcreteNotification allKeys] 错误。改变这个:

    -(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
    

    到这里:

    -(void)didReceiveRemoteNotification:(NSNotification*)notif {
    NSDictionary *dict = notif.userInfo;
    }
    

    【讨论】:

      【解决方案6】:

      我一直喜欢涉及类别的解决方案,因为它们是固定的并且可以轻松重复使用。

      所以我在 UIWindow 上创建了一个类别。您现在可以在 UIWindow 上调用 visibleViewController,这将通过向下搜索控制器层次结构为您提供可见视图控制器。如果您使用导航和/或标签栏控制器,这将有效。如果您有其他类型的控制器可以推荐,请告诉我,我可以添加。

      UIWindow+PazLabs.h(头文件)

      #import <UIKit/UIKit.h>
      
      @interface UIWindow (PazLabs)
      
      - (UIViewController *) visibleViewController;
      
      @end
      

      UIWindow+PazLabs.m(实现文件)

      #import "UIWindow+PazLabs.h"
      
      @implementation UIWindow (PazLabs)
      
      - (UIViewController *)visibleViewController {
          UIViewController *rootViewController = self.rootViewController;
          return [UIWindow getVisibleViewControllerFrom:rootViewController];
      }
      
      + (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
          if ([vc isKindOfClass:[UINavigationController class]]) {
              return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
          } else if ([vc isKindOfClass:[UITabBarController class]]) {
              return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
          } else {
              if (vc.presentedViewController) {
                  return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];
              } else {
                  return vc;
              }
          }
      }
      
      @end
      

      斯威夫特版本

      public extension UIWindow {
          public var visibleViewController: UIViewController? {
              return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
          }
      
          public static func getVisibleViewControllerFrom(_ vc: UIViewController?) -> UIViewController? {
              if let nc = vc as? UINavigationController {
                  return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
              } else if let tc = vc as? UITabBarController {
                  return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
              } else {
                  if let pvc = vc?.presentedViewController {
                      return UIWindow.getVisibleViewControllerFrom(pvc)
                  } else {
                      return vc
                  }
              }
          }
      }
      

      【讨论】:

      • 我如何将它用于 swift 版本?
      • 我无法理解您的问题。复制并粘贴到您的代码中。
      • 自定义容器 VC 怎么样?
      • @Mingming 添加额外的 if 来检查它是否是自定义容器 VC(在 getVisibielController 方法中)应该不难,如果是,则返回“可见”控制器,这通常是vc.childControllers.lastObject 适用于大多数自定义容器 VC 实现(我想),但取决于它的实现方式。
      • 我刚刚发布了一个与此答案相同的答案,除了更新的语法:它使用 switch-case 并遵循 Swift 3 命名约定:stackoverflow.com/a/42486823/3451975
      【解决方案7】:

      我发现 iOS 8 把一切都搞砸了。在 iOS 7 中,每当您以模态方式呈现 UINavigationController 时,视图层次结构上都会有一个新的 UITransitionView。无论如何,这是我发现获得最高 VC 的代码。调用getTopMostViewController 应该会返回一个VC,您应该能够发送类似presentViewController:animated:completion 的消息。其目的是为您提供一个可用于呈现模态 VC 的 VC,因此它很可能会在 UINavigationController 之类的容器类而不是其中包含的 VC 处停止并返回。也应该不难调整代码来做到这一点。我已经在 iOS 6、7 和 8 的各种情况下测试了这段代码。如果发现错误,请告诉我。

      + (UIViewController*) getTopMostViewController
      {
          UIWindow *window = [[UIApplication sharedApplication] keyWindow];
          if (window.windowLevel != UIWindowLevelNormal) {
              NSArray *windows = [[UIApplication sharedApplication] windows];
              for(window in windows) {
                  if (window.windowLevel == UIWindowLevelNormal) {
                      break;
                  }
              }
          }
      
          for (UIView *subView in [window subviews])
          {
              UIResponder *responder = [subView nextResponder];
      
              //added this block of code for iOS 8 which puts a UITransitionView in between the UIWindow and the UILayoutContainerView
              if ([responder isEqual:window])
              {
                  //this is a UITransitionView
                  if ([[subView subviews] count])
                  {
                      UIView *subSubView = [subView subviews][0]; //this should be the UILayoutContainerView
                      responder = [subSubView nextResponder];
                  }
              }
      
              if([responder isKindOfClass:[UIViewController class]]) {
                  return [self topViewController: (UIViewController *) responder];
              }
          }
      
          return nil;
      }
      
      + (UIViewController *) topViewController: (UIViewController *) controller
      {
          BOOL isPresenting = NO;
          do {
              // this path is called only on iOS 6+, so -presentedViewController is fine here.
              UIViewController *presented = [controller presentedViewController];
              isPresenting = presented != nil;
              if(presented != nil) {
                  controller = presented;
              }
      
          } while (isPresenting);
      
          return controller;
      }
      

      【讨论】:

      • 请不要重复答案 - 如果问题重复,请将其标记为重复,或者如果不重复,则使用他们应得的具体答案回答个别问题。
      【解决方案8】:

      zirinisp 在 Swift 中的回答:

      extension UIWindow {
      
          func visibleViewController() -> UIViewController? {
              if let rootViewController: UIViewController  = self.rootViewController {
                  return UIWindow.getVisibleViewControllerFrom(rootViewController)
              }
              return nil
          }
      
          class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
      
              if vc.isKindOfClass(UINavigationController.self) {
      
                  let navigationController = vc as UINavigationController
                  return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)
      
              } else if vc.isKindOfClass(UITabBarController.self) {
      
                  let tabBarController = vc as UITabBarController
                  return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!)
      
              } else {
      
                  if let presentedViewController = vc.presentedViewController {
      
                      return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)
      
                  } else {
      
                      return vc;
                  }
              }
          }
      }
      

      用法:

       if let topController = window.visibleViewController() {
                  println(topController)
              }
      

      【讨论】:

      • Swift 2.0 是 as!navigationController.visibleViewController!
      【解决方案9】:

      Swift 中 UIApplication 的简单扩展(甚至关心 iPhone 上 UITabBarController 内的 moreNavigationController)

      extension UIApplication {
          class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
      
              if let nav = base as? UINavigationController {
                  return topViewController(base: nav.visibleViewController)
              }
      
              if let tab = base as? UITabBarController {
                  let moreNavigationController = tab.moreNavigationController
      
                  if let top = moreNavigationController.topViewController where top.view.window != nil {
                      return topViewController(top)
                  } else if let selected = tab.selectedViewController {
                      return topViewController(selected)
                  }
              }
      
              if let presented = base?.presentedViewController {
                  return topViewController(base: presented)
              }
      
              return base
          }
      }
      

      简单用法:

          if let rootViewController = UIApplication.topViewController() {
              //do sth with root view controller
          }
      

      完美运行:-)

      更新干净的代码:

      extension UIViewController {
          var top: UIViewController? {
              if let controller = self as? UINavigationController {
                  return controller.topViewController?.top
              }
              if let controller = self as? UISplitViewController {
                  return controller.viewControllers.last?.top
              }
              if let controller = self as? UITabBarController {
                  return controller.selectedViewController?.top
              }
              if let controller = presentedViewController {
                  return controller.top
              }
              return self
          }
      }
      

      【讨论】:

      • 这似乎是 Swift 2.x 的代码。 Swift 3.x 不再有“where”。此外,“sharedApplication()”现在是“共享的”。没什么大不了的。更新只需一分钟。提到它使用递归可能会很好。此外,对 topViewController 的每次调用都应该需要“base:”前缀。
      【解决方案10】:

      这对我有用。我有许多具有不同控制器的目标,因此以前的答案似乎不起作用。

      首先你想在你的 AppDelegate 类中使用这个:

      var window: UIWindow?
      

      然后,在你的函数中

      let navigationController = window?.rootViewController as? UINavigationController
      if let activeController = navigationController!.visibleViewController {
          if activeController.isKindOfClass( MyViewController )  {
              println("I have found my controller!")    
         }
      }
      

      【讨论】:

        【解决方案11】:

        只是对@zirinisp 答案的补充。

        创建一个文件,命名为UIWindowExtension.swift并粘贴以下sn-p:

        import UIKit
        
        public extension UIWindow {
            public var visibleViewController: UIViewController? {
                return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
            }
        
            public static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
                if let nc = vc as? UINavigationController {
                    return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
                } else if let tc = vc as? UITabBarController {
                    return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
                } else {
                    if let pvc = vc?.presentedViewController {
                        return UIWindow.getVisibleViewControllerFrom(pvc)
                    } else {
                        return vc
                    }
                }
            }
        }
        
        func getTopViewController() -> UIViewController? {
            let appDelegate = UIApplication.sharedApplication().delegate
            if let window = appDelegate!.window {
                return window?.visibleViewController
            }
            return nil
        }
        

        在任何地方使用它:

        if let topVC = getTopViewController() {
        
        }
        

        感谢@zirinisp。

        【讨论】:

          【解决方案12】:

          比所有其他解决方案的代码少得多:

          Objective-C 版本:

          - (UIViewController *)getTopViewController {
              UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
              while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController;
          
              return topViewController;
          }
          

          Swift 2.0 版本:(感谢 Steve.B)

          func getTopViewController() -> UIViewController {
              var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
              while (topViewController.presentedViewController != nil) {
                  topViewController = topViewController.presentedViewController!
              }
              return topViewController
          }
          

          可以在您的应用中的任何位置使用,即使是模态框也是如此。

          【讨论】:

          • 这不处理呈现的视图控制器是 UINavigationController 的情况,它有自己的孩子。
          • @levigroker,也许这就是您构建视图的方式?将它与 Nav 一起使用对我来说效果很好。 (我就是这么用的)
          • @jungledev 我相信你是对的。也就是说,需要一个适用于所有视图控制器配置的解决方案。
          • @levigroker 它确实适用于所有标准的 vc 配置——我工作的应用程序具有非常复杂的架构,被超过 50 万用户使用,这适用于应用程序。也许您应该通过代码示例发布一个问题,询问为什么它在您的视图中不起作用?
          • jungledev 我很高兴这段代码对你有用,但它似乎不是一个完整的解决方案。 @zirinisp 的回答非常适合我的情况。
          【解决方案13】:

          Swift 2.0 版本的jungledev的回答

          func getTopViewController() -> UIViewController {
              var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
              while (topViewController.presentedViewController != nil) {
                  topViewController = topViewController.presentedViewController!
              }
              return topViewController
          }
          

          【讨论】:

            【解决方案14】:

            这是我尝试过的最好的方法。如果它应该帮助任何人......

            + (UIViewController*) topMostController
            {
                UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
            
                while (topController.presentedViewController) {
                    topController = topController.presentedViewController;
                }
            
                return topController;
            }
            

            【讨论】:

              【解决方案15】:

              我使用visibleViewControllers 属性为UIApplication 创建了一个类别。主要思想很简单。我在UIViewController 中混合了viewDidAppearviewDidDisappear 方法。在 viewDidAppear 方法中 viewController 被添加到堆栈中。在viewDidDisappear 方法中,viewController 被从堆栈中移除。使用NSPointerArray 代替NSArray 来存储弱UIViewController 的引用。这种方法适用于任何 viewControllers 层次结构。

              UIApplication+VisibleViewControllers.h

              #import <UIKit/UIKit.h>
              
              @interface UIApplication (VisibleViewControllers)
              
              @property (nonatomic, readonly) NSArray<__kindof UIViewController *> *visibleViewControllers;
              
              @end
              

              UIApplication+VisibleViewControllers.m

              #import "UIApplication+VisibleViewControllers.h"
              #import <objc/runtime.h>
              
              @interface UIApplication ()
              
              @property (nonatomic, readonly) NSPointerArray *visibleViewControllersPointers;
              
              @end
              
              @implementation UIApplication (VisibleViewControllers)
              
              - (NSArray<__kindof UIViewController *> *)visibleViewControllers {
                  return self.visibleViewControllersPointers.allObjects;
              }
              
              - (NSPointerArray *)visibleViewControllersPointers {
                  NSPointerArray *pointers = objc_getAssociatedObject(self, @selector(visibleViewControllersPointers));
                  if (!pointers) {
                      pointers = [NSPointerArray weakObjectsPointerArray];
                      objc_setAssociatedObject(self, @selector(visibleViewControllersPointers), pointers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
                  }
                  return pointers;
              }
              
              @end
              
              @implementation UIViewController (UIApplication_VisibleViewControllers)
              
              + (void)swizzleMethodWithOriginalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
                  Method originalMethod = class_getInstanceMethod(self, originalSelector);
                  Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
                  BOOL didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
                  if (didAddMethod) {
                      class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
                  } else {
                      method_exchangeImplementations(originalMethod, swizzledMethod);
                  }
              }
              
              + (void)load {
                  static dispatch_once_t onceToken;
                  dispatch_once(&onceToken, ^{
                      [self swizzleMethodWithOriginalSelector:@selector(viewDidAppear:)
                                             swizzledSelector:@selector(uiapplication_visibleviewcontrollers_viewDidAppear:)];
                      [self swizzleMethodWithOriginalSelector:@selector(viewDidDisappear:)
                                             swizzledSelector:@selector(uiapplication_visibleviewcontrollers_viewDidDisappear:)];
                  });
              }
              
              - (void)uiapplication_visibleviewcontrollers_viewDidAppear:(BOOL)animated {
                  [[UIApplication sharedApplication].visibleViewControllersPointers addPointer:(__bridge void * _Nullable)self];
                  [self uiapplication_visibleviewcontrollers_viewDidAppear:animated];
              }
              
              - (void)uiapplication_visibleviewcontrollers_viewDidDisappear:(BOOL)animated {
                  NSPointerArray *pointers = [UIApplication sharedApplication].visibleViewControllersPointers;
                  for (int i = 0; i < pointers.count; i++) {
                      UIViewController *viewController = [pointers pointerAtIndex:i];
                      if ([viewController isEqual:self]) {
                          [pointers removePointerAtIndex:i];
                          break;
                      }
                  }
                  [self uiapplication_visibleviewcontrollers_viewDidDisappear:animated];
              }
              
              @end
              

              https://gist.github.com/medvedzzz/e6287b99011f2437ac0beb5a72a897f0

              Swift 3 版本

              UIApplication+VisibleViewControllers.swift

              import UIKit
              
              extension UIApplication {
              
                  private struct AssociatedObjectsKeys {
                      static var visibleViewControllersPointers = "UIApplication_visibleViewControllersPointers"
                  }
              
                  fileprivate var visibleViewControllersPointers: NSPointerArray {
                      var pointers = objc_getAssociatedObject(self, &AssociatedObjectsKeys.visibleViewControllersPointers) as! NSPointerArray?
                      if (pointers == nil) {
                          pointers = NSPointerArray.weakObjects()
                          objc_setAssociatedObject(self, &AssociatedObjectsKeys.visibleViewControllersPointers, pointers, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                      }
                      return pointers!
                  }
              
                  var visibleViewControllers: [UIViewController] {
                      return visibleViewControllersPointers.allObjects as! [UIViewController]
                  }
              }
              
              extension UIViewController {
              
                  private static func swizzleFunc(withOriginalSelector originalSelector: Selector, swizzledSelector: Selector) {
                      let originalMethod = class_getInstanceMethod(self, originalSelector)
                      let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
                      let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
                      if didAddMethod {
                          class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
                      } else {
                          method_exchangeImplementations(originalMethod, swizzledMethod);
                      }
                  }
              
                  override open class func initialize() {
                      if self != UIViewController.self {
                          return
                      }
                      let swizzlingClosure: () = {
                          UIViewController.swizzleFunc(withOriginalSelector: #selector(UIViewController.viewDidAppear(_:)),
                                                       swizzledSelector: #selector(uiapplication_visibleviewcontrollers_viewDidAppear(_:)))
                          UIViewController.swizzleFunc(withOriginalSelector: #selector(UIViewController.viewDidDisappear(_:)),
                                                       swizzledSelector: #selector(uiapplication_visibleviewcontrollers_viewDidDisappear(_:)))
                      }()
                      swizzlingClosure
                  }
              
                  @objc private func uiapplication_visibleviewcontrollers_viewDidAppear(_ animated: Bool) {
                      UIApplication.shared.visibleViewControllersPointers.addPointer(Unmanaged.passUnretained(self).toOpaque())
                      uiapplication_visibleviewcontrollers_viewDidAppear(animated)
                  }
              
                  @objc private func uiapplication_visibleviewcontrollers_viewDidDisappear(_ animated: Bool) {
                      let pointers = UIApplication.shared.visibleViewControllersPointers
                      for i in 0..<pointers.count {
                          if let pointer = pointers.pointer(at: i) {
                              let viewController = Unmanaged<AnyObject>.fromOpaque(pointer).takeUnretainedValue() as? UIViewController
                              if viewController.isEqual(self) {
                                  pointers.removePointer(at: i)
                                  break
                              }
                          }
                      }
                      uiapplication_visibleviewcontrollers_viewDidDisappear(animated)
                  }
              }
              

              https://gist.github.com/medvedzzz/ee6f4071639d987793977dba04e11399

              【讨论】:

                【解决方案16】:

                代码

                这是在 Swift 3/4/5 中使用出色的 switch-case 语法 的方法:

                import UIKit
                
                extension UIWindow {
                    /// Returns the currently visible view controller if any reachable within the window.
                    public var visibleViewController: UIViewController? {
                        return UIWindow.visibleViewController(from: rootViewController)
                    }
                
                    /// Recursively follows navigation controllers, tab bar controllers and modal presented view controllers starting
                    /// from the given view controller to find the currently visible view controller.
                    ///
                    /// - Parameters:
                    ///   - viewController: The view controller to start the recursive search from.
                    /// - Returns: The view controller that is most probably visible on screen right now.
                    public static func visibleViewController(from viewController: UIViewController?) -> UIViewController? {
                        switch viewController {
                        case let navigationController as UINavigationController:
                            return UIWindow.visibleViewController(from: navigationController.visibleViewController ?? navigationController.topViewController)
                
                        case let tabBarController as UITabBarController:
                            return UIWindow.visibleViewController(from: tabBarController.selectedViewController)
                
                        case let presentingViewController where viewController?.presentedViewController != nil:
                            return UIWindow.visibleViewController(from: presentingViewController?.presentedViewController)
                
                        default:
                            return viewController
                        }
                    }
                }
                

                基本思想与 zirinisp 的答案相同,只是使用了更类似于 Swift 3+ 的语法。


                用法

                您可能想要创建一个名为UIWindowExt.swift 的文件并将上述扩展代码复制到其中。

                在调用端,它可以在没有任何特定视图控制器的情况下使用

                if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
                    // do whatever you want with your `visibleViewCtrl`
                }
                

                或者,如果您知道您的可见视图控制器可以从特定视图控制器访问:

                if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
                    // do whatever you want with your `visibleViewCtrl`
                }
                

                希望对你有帮助!

                【讨论】:

                • 第三种情况会因为无限递归而崩溃。修复方法是将 vc 重命名为 presentingViewController 并将 presentingViewController.presentedViewController 作为参数传递给递归方法。
                • 我不太明白,抱歉。你的意思是UIWindow.visibleViewController(from: presentedViewController) 应该是UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
                • 正确,presentedViewControllerviewController 是同一个对象,它将自己调用该方法,直到堆栈溢出(双关语)。所以它会是case let presentingViewController where viewController?.presentedViewController != nil: return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
                • 这个解决方案在其他人没有的情况下有效。您应该更新到 Swift 5。基本上没有变化。只需更新您的答案的标题。
                【解决方案17】:
                extension UIApplication {
                    /// The top most view controller
                    static var topMostViewController: UIViewController? {
                        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
                    }
                }
                
                extension UIViewController {
                    /// The visible view controller from a given view controller
                    var visibleViewController: UIViewController? {
                        if let navigationController = self as? UINavigationController {
                            return navigationController.topViewController?.visibleViewController
                        } else if let tabBarController = self as? UITabBarController {
                            return tabBarController.selectedViewController?.visibleViewController
                        } else if let presentedViewController = presentedViewController {
                            return presentedViewController.visibleViewController
                        } else {
                            return self
                        }
                    }
                }
                

                有了这个,你可以很容易地得到这样的顶部视图控制器

                let viewController = UIApplication.topMostViewController
                

                需要注意的是,如果当前正在显示 UIAlertController,UIApplication.topMostViewController 将返回 UIAlertController

                【讨论】:

                  【解决方案18】:

                  我的更好! :)

                  extension UIApplication {
                      var visibleViewController : UIViewController? {
                          return keyWindow?.rootViewController?.topViewController
                      }
                  }
                  
                  extension UIViewController {
                      fileprivate var topViewController: UIViewController {
                          switch self {
                          case is UINavigationController:
                              return (self as! UINavigationController).visibleViewController?.topViewController ?? self
                          case is UITabBarController:
                              return (self as! UITabBarController).selectedViewController?.topViewController ?? self
                          default:
                              return presentedViewController?.topViewController ?? self
                          }
                      }
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    如果您正在运行带有调试或发布的应用,请务必检查您的构建配置。

                    重要提示:除非在调试模式下运行您的应用,否则您无法对其进行测试

                    这是我的解决方案

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2017-04-25
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-03-05
                      • 1970-01-01
                      • 2017-04-04
                      相关资源
                      最近更新 更多