在AppDelegate.m中获取屏幕上当前显示的UIViewController


126

UIViewController屏幕上的当前电流需要通过设置一些徽标视图来响应来自APN的推送通知。但是我怎么能获得UIViewControllerin方法application:didReceiveRemoteNotification:of AppDelegate.m

我尝试使用self.window.rootViewController获取当前的显示UIViewController,它可能是一个UINavigationViewController或其他某种类型的视图控制器。而且我发现的visibleViewController属性UINavigationViewController可用于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

}

Answers:


99

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

UIViewController *vc = self.window.rootViewController;

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

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

编辑:

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

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

尽管此视图可能是不可见的,甚至被其某些子视图所覆盖...

同样,这取决于您的UI,但这可能会有所帮助...


19
问题在于如果可见视图不属于根视图控制器(在模式视图等情况下)。
迪马2012年

是的,我愿意。但是它可能是一个UITabViewController。是否有直接方法可以在屏幕上获取UIViewController?
陆元

2
好了,您知道,UINavigationController提供了一种方法,让您知道哪个控制器是最高的。您的根控制器应该以某种方式提供相同的信息。通常无法推断出它,因为它严格取决于您构建UI的方式,并且没有显式的控制器层次结构(就像视图中那样)。您可以仅在根控制器上添加一个属性,并在每次将“新”控制器“推入”顶部时设置其值。
sergio

1
只要值保持最新,那对我来说也是一种好方法。
迪马2012年

4
没有直接从UIView实例访问控制器的方法。rootViewController必然当前显示的控制器。它只是视图层次结构的顶部。
Gingi 2014年

101

我一直很喜欢涉及类别的解决方案,因为这些类别很固定并且可以轻松重用。

因此,我在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
            }
        }
    }
}

2
如何将其用于快速版本?
维杰·辛格·拉纳

2
我不明白你的问题。复制并粘贴到您的代码中。
zirinisp 2015年

自定义容器VC呢?
Mingming 2015年

@Mingming,如果要检查其自定义容器VC(在getVisibielController方法中)是否为自定义容器VC,并返回“可见”控制器(对于大多数自定义,通常为vc.childControllers.lastObject),则应该不难添加容器VC的实现(我想),但是将取决于其实现方式。
加杜

1
我刚刚发布了一个与该答案相同的答案,但语法
有所

43

Swift中UIApplication的简单扩展(甚至可以UITabBarController在iPhone上关注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
    }
}

1
这似乎是Swift 2.x的代码。Swift 3.x不再具有“ where”。另外,“ sharedApplication()”现在是“共享的”。没什么大不了的。只需一分钟即可更新。最好提及它使用递归。另外,每次对topViewController的调用都需要使用“ base:”前缀。
杰夫·缪尔

37

您也可以通过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];
}

在每个View Controller中:

-(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方法中处理添加/删除观察者调用。


1
addObserver:bar里面是什么viewDidLoad?我必须替换为self吗?
CainaSouza

感谢您指出-这应该是自我。我将更新答案。
2013年

从userInfo获取所有密钥时崩溃。[NSConcreteNotification allKeys]:无法识别的选择器已发送到实例0x1fd87480 2013-07-05 16:10:3​​6.469 Providence [2961:907] ***由于未捕获的异常'NSInvalidArgumentException'而终止了应用程序,原因:'-[NSConcreteNotification allKeys]:无法识别选择发送到实例0x1fd87480'
AWAIS塔里克

@AwaisTariq-嗯-我的猜测是,如接口所指定,iOS传递给didReceiveRemoteNotification的对象实际上不是NSDictionary。
Aneil Mallavarapu

如果用户尚未导航到您的观察员班怎么办?:/
halbano 2015年

15

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

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+的语法。


用法

您可能要创建一个名为的文件UIWindowExtension.swift。确保它包含该import UIKit语句,现在复制上面的扩展代码

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

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)
Jeehut

正确,presentedViewController并且viewController是同一个对象,它将与自身一起调用方法,直到堆栈溢出(双关型)。它将是 case let presentingViewController where viewController?.presentedViewController != nil: return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
Ikhsan Assaat'5

1
当其他人没有解决方案时,此解决方案就会起作用。您应该更新到Swift5。基本上没有变化。只需更新标题即可获得答案。
TM Lynch

14

我发现iOS 8搞砸了一切。在iOS 7中,UITransitionView只要有模态呈现,视图层次结构中就会有一个新功能UINavigationController。无论如何,这是我找到的代码获得的最高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;
}

请不要重复答案-如果问题重复,则将其标记为重复;如果不是重复问题,则应使用应提供的特定答案回答单个问题。
Flexo

13

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

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
}

即使在模式下也可以在您的应用程序中的任何地方工作。


1
这不能解决呈现的视图控制器是UINavigationController具有自己的子代的视图控制器的情况。
levigroker '16

@levigroker,也许这就是您构建视图的方式吗?将它与Nav一起使用对我来说效果很好。(这就是我的使用方式)
jungledev

@jungledev我确定你是正确的。也就是说,需要一种适用于所有视图控制器配置的解决方案。
levigroker '16

@levigroker它确实在所有标准VC工作设置-上有一个非常复杂的体系结构中的应用程序,我的工作,是由超过50万用户使用,并且该作品无处不在的应用程序。也许您应该通过代码示例发布一个问题,询问为什么它在您看来不起作用?
jungledev '16

jungledev我很高兴这个代码对您有用,但是它似乎并不是一个完整的解决方案。@zirinisp的答案适合我的情况。
levigroker '16

8

Swift中zirinisp的答案:

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

as!navigationController.visibleViewController!对雨燕2.0
LinusGeffarth

7

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

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

然后像这样按标题来检查

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

最好的答案是dntnt,这也是您最好的命名方式:self.title = myPhotoView
Resty 2014年

5

我的更好!:)

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

4

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

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


是的,它与视图有关,因为我必须显示徽章视图。让我检查一下链接。谢谢:)
陆元

4

只是除了@zirinisp答案。

创建一个文件,命名UIWindowExtension.swift并粘贴以下代码段:

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。


3

关于上面的NSNotificationCenter发表(对不起,找不到在下面发表评论的地方...)

如果有些人遇到了-[NSConcreteNotification allKeys]错误。更改此:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo

对此:

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

3

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

首先,您需要在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!")    
   }
}

2

这是我尝试过的最佳方法。如果它可以帮助任何人...

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

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    return topController;
}

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

有了这个,您可以像这样轻松获得顶级的post view controller

let viewController = UIApplication.topMostViewController

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


1

Swift 2.0版本的Jungledev的答案

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

1

UIApplication使用visibleViewControllers属性创建了一个类别。主要思想很简单。我搅和viewDidAppearviewDidDisappear的方法UIViewController。在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


1

如果要通过调试或发行版运行应用程序,请始终检查构建配置。

重要说明:如果不以调试模式运行应用程序,则无法对其进行测试

这是我的解决方案

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.