AppDelegate.mの画面に現在表示されているUIViewControllerを取得します。


126

UIViewController画面上の電流は、いくつかのバッジビューを設定することにより、APNからのプッシュ通知に応答する必要があります。しかし、どのように私は得ることができるUIViewController方法でapplication:didReceiveRemoteNotification:で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

}

回答:


99

rootViewControllerコントローラがでない場合にもを使用できますUINavigationController

UIViewController *vc = self.window.rootViewController;

ルートビューコントローラーがわかったら、UIの構築方法に依存しますが、コントローラー階層をナビゲートする方法を見つけることができます。

アプリの定義方法についてもう少し詳しく教えていただければ、もう少しヒントを教えてください。

編集:

最上位のビュー(ビューコントローラーではない)が必要な場合は、

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

このビューは非表示であるか、一部のサブビューで覆われている可能性がありますが...

繰り返しますが、それはあなたのUIに依存しますが、これは役立つかもしれません...


19
この問題は、表示されているビューがルートビューコントローラーに属していない場合です(モーダルビューなどの場合)。
ディマ

はい、そうです。しかし、それはおそらくUITabViewControllerです。画面にUIViewControllerを取得する直接メソッドはありませんか?
lu元

2
そうですね、UINavigationControllerは、どのコントローラーが最上位にあるかを知る方法を提供します。ルートコントローラは同じ情報を何らかの方法で提供する必要があります。これは、UIの構築方法に厳密に依存し、明示的なコントローラー階層がない(ビューで発生するような)ため、一般に推測できません。ルートコントローラーにプロパティを追加し、新しいコントローラーを「プッシュ」するたびにその値を設定するだけです。
sergio 2012

1
値が最新に保たれている限り、それも私に行く良い方法のようです。
ディマ

4
UIViewインスタンスからコントローラーに直接アクセスする方法はありません。現在表示されているコントローラrootViewControllerとは限りません。ビュー階層の最上位にあります。
ギンギ

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

Swiftバージョン

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
これを迅速なバージョンに使用するにはどうすればよいですか?
Vijay Singh Rana

2
あなたの質問は理解できません。コード内にコピーして貼り付けます。
zirinisp 2015

カスタムコンテナVCについてはどうですか?
Mingming、2015

@MingmingカスタムコンテナVC(getVisibielControllerメソッド内)であるかどうかを確認し、そうである場合は「可視」コントローラーを返す場合、通常、ほとんどのカスタムの場合はvc.childControllers.lastObjectになります。コンテナVCの実装(私はそう思う)が、その実装方法に依存します。
gadu

1
私はちょうど更新構文を除いて、この答えと同じアプローチで答えを投稿:これは、スイッチケースを使用してだとスウィフト3命名規則に従います。stackoverflow.com/a/42486823/3451975
Jeehut

43

SwiftのUIApplicationの単純な拡張機能UITabBarControlleriPhone 内の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()」は「共有」になりました。大したことではない。更新にはわずか1分しかかかりません。再帰を使用していることを述べておくとよいでしょう。また、topViewControllerを呼び出すたびに、「base:」接頭辞が必要です。
Jeff Muir 2017

37

NSNotificationCenterを介して通知を投稿することもできます。これにより、たとえばモーダルが提示されている場合など、View Controller階層をトラバースすることが難しい場合がある多くの状況に対処できます。

例えば、

// 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メソッドでadd / removeオブザーバー呼び出しを処理します。


1
何がaddObserver:bar入ってるのviewDidLoad?に置き換える必要がありselfますか?
CainaSouza 2013年

それを指摘してくれてありがとう-それは自己でなければなりません。答えを更新します。
Aneil Mallavarapu 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 2013年

ユーザーがまだオブザーバークラスに移動していない場合はどうなりますか?:/
halbano

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の回答と同じです。Swift3以上の構文を使用しているだけです。


使用法

おそらく、という名前のファイルを作成する必要がありますUIWindowExtension.swiftimport UIKitステートメントが含まれていることを確認し、上記の拡張コードをコピーします

呼び出し側では、特定のView Controllerなしで使用できます。

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

お役に立てば幸いです。


3番目のケースは、無限再帰のためにクラッシュします。修正は、vcの名前を変更し、パラメーターとして再帰的メソッドにpresentingViewController渡すpresentingViewController.presentedViewControllerことです。
Ikhsan Assaat

うまく聞き取れませんでした。あなたは意味UIWindow.visibleViewController(from: presentedViewController)の代わりにする必要がありますかUIWindow.visibleViewController(from: presentingViewController.presentedViewController)
Jeehut 2017年

正しい、presentedViewControllerviewController同じオブジェクトであり、それはスタックオーバーフローするまで自身でメソッドを呼び出す(しゃれは意図しました)。だからでしょう case let presentingViewController where viewController?.presentedViewController != nil: return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
Ikhsanアサアット

1
このソリューションは、他のソリューションが機能しなかったときに機能しました。Swift 5に更新する必要があります。基本的に変更はありません。回答のヘッダーを更新してください。
TMリンチ

14

私はiOS 8がすべてを台無しにしたことを発見しました。iOS 7ではUITransitionView、モーダルプレゼンテーションを行うたびに、ビュー階層に新しいものがありますUINavigationController。とにかく、これが最上位のVCを取得するコードです。を呼び出すとgetTopMostViewController、のようなメッセージを送信できるはずのVCが返されますpresentViewController:animated:completion。その目的は、モーダルVCを提示するために使用できるVCを取得することです。そのため、コンテナに含まれるVCではなく、のようなコンテナクラスで停止して戻る可能性がありUINavigationControllerます。それを行うためにコードを適合させることも難しくないはずです。このコードを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 2016

@levigroker、おそらくそれはあなたがあなたの見解を構築した方法ですか?ナビでこれを使用するのは問題ありません。(それが私がそれを使用している方法です)
jungledev

@jungledev私はあなたが正しいと確信しています。とはいえ、すべてのView Controller構成で機能するソリューションが必要です。
levigroker 2016

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

Dfntly最良の答えは、また、uがあなたのViewControllerをして名前を付けることができます:self.title = myPhotoView
Resty

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は、ビューのプロパティに値があるかどうかを確認します。詳細はこちら


はい、バッジビューを表示する必要があるため、ビューに関連しています。リンクを確認させてください。ありがとうございます:)
lu元

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 Postについて(申し訳ありませんが、その下にコメントを投稿する場所を見つけることができません...)

一部のユーザーが並べ替えの-[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
        }
    }
}

これにより、トップポストビューコントローラーを簡単に取得できます。

let viewController = UIApplication.topMostViewController

注意すべき点の1つは、現在表示されているUIAlertControllerがある場合、UIApplication.topMostViewControllerが返されることUIAlertControllerです。


1

jungledevの回答のSwift 2.0バージョン

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

1

UIApplicationwith visibleViewControllersプロパティのカテゴリを作成しました。主なアイデアはかなりシンプルです。私はスウィズルしviewDidAppearviewDidDisappearメソッドをUIViewControllerviewDidAppear方法のViewControllerをスタックに追加されます。viewDidDisappear方法のViewControllerをスタックから除去されます。ウィークの参照を格納するNSPointerArray代わりに使用されます。このアプローチは、すべてのviewControllers階層で機能します。NSArrayUIViewController

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

アプリをデバッグまたはリリースで実行している場合は、常にビルド構成を確認してください。

重要な注意:デバッグモードでアプリを実行せずにテストすることはできません

これが私の解決策でした

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.