133

画面上の現在UIViewControllerは、いくつかのバッジ ビューを設定することによって、APNs からのプッシュ通知に応答する必要があります。しかし、どうすればUIViewControllerin メソッドapplication:didReceiveRemoteNotification: ofを取得できAppDelegate.mますか?

self.window.rootViewController現在の表示を取得するために使用しようとしましたがUIViewController、それはUINavigationViewControllerまたは他の種類のView Controllerである可能性があります。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

}
4

19 に答える 19

102

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

UIViewController *vc = self.window.rootViewController;

ルート ビュー コントローラーがわかれば、UI をどのように構築したかによって異なりますが、コントローラー階層をナビゲートする方法を見つけることができる可能性があります。

アプリの定義方法についてもう少し詳しく教えていただければ、さらにヒントが得られるかもしれません。

編集:

最上位のビュー(View Controllerではなく)が必要な場合は、チェックできます

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

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

繰り返しますが、UI によって異なりますが、これが役立つ場合があります...

于 2012-07-24T19:14:37.477 に答える
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];
}

各ビュー コントローラーで次の操作を行います。

-(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 オブザーバー呼び出しを処理します。

于 2013-04-21T14:52:43.340 に答える
14

iOS 8 がすべてを台無しにしてしまったことがわかりました。iOS 7 ではUITransitionView、モーダルに表示された がある場合はいつでも、ビュー階層に new があります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;
}
于 2014-09-19T23:15:53.963 に答える
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
}

モーダルであっても、アプリ内のどこでも機能します。

于 2016-06-04T18:40:07.093 に答える
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)
        }
于 2014-12-19T14:59:36.137 に答える
7

各 ViewController にタイトルを指定し、以下のコードで現在の ViewController のタイトルを取得します。

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

次に、このようにタイトルで確認してください

  if([currentController isEqualToString:@"myViewControllerTitle"]){
    //write your code according to View controller.
  }
}
于 2013-08-19T09:19:27.457 に答える
4

アプリのデリゲートでプッシュ通知コードを処理しないのはなぜですか? ビューに直接関係していますか?

windowビューのプロパティに値があるかどうかを確認することで、UIViewController のビューが現在表示されているかどうかを確認できます。詳しくはこちらをご覧ください

于 2012-07-24T19:16:46.283 に答える
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!")    
   }
}
于 2015-04-23T19:57:20.323 に答える
2

これは私が試した最善の方法です。それが誰かの助けになるなら...

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

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

    return topController;
}
于 2016-11-23T09:51:09.020 に答える