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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11637709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:25:31  来源:igfitidea点击:

Get the current displaying UIViewController on the screen in AppDelegate.m

iphoneiosuiviewcontrollerpush-notification

提问by lu yuan

The current UIViewControlleron the screen need to response to push-notifications from APNs, by setting some badge views. But how could I get the UIViewControllerin methodapplication:didReceiveRemoteNotification: of AppDelegate.m?

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

I tried use self.window.rootViewControllerto get the current displaying UIViewController, it may be a UINavigationViewControlleror some other kind of view controller. And I find out that the visibleViewControllerproperty of UINavigationViewControllercan be used to get the UIViewControlleron the screen. But what could I do if it is not a UINavigationViewController?

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

Any help is appreciated! The related code is as following.

任何帮助表示赞赏!相关代码如下。

AppDelegate.m

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

视图控制器A.m

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

    //set some badge view here

}

采纳答案by sergio

You can use the rootViewControlleralso when your controller is not a UINavigationController:

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

UIViewController *vc = self.window.rootViewController;

Once you know the root view controller, then it depends on how you have built your UI, but you can possibly find out a way to navigate through the controllers hierarchy.

一旦你知道了根视图控制器,那么这取决于你如何构建你的 UI,但你可能会找到一种方法来浏览控制器层次结构。

If you give some more details about the way you defined your app, then I might give some more hint.

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

EDIT:

编辑:

If you want the topmost view(not view controller), you could check

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

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

although this view might be invisible or even covered by some of its subviews...

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

again, it depends on your UI, but this might help...

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

回答by zirinisp

I always love solutions that involve categories as they are bolt on and can be easily reused.

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

So I created a category on UIWindow. You can now call visibleViewController on UIWindow and this will get you the visible view controller by searching down the controller hierarchy. This works if you are using navigation and/or tab bar controller. If you have another type of controller to suggest please let me know and I can add it.

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

UIWindow+PazLabs.h (header file)

UIWindow+PazLabs.h(头文件)

#import <UIKit/UIKit.h>

@interface UIWindow (PazLabs)

- (UIViewController *) visibleViewController;

@end

UIWindow+PazLabs.m (implementation file)

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 Version

迅捷版

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

回答by Bart?omiej Semańczyk

Simple extension for UIApplication in Swift (cares even about moreNavigationController within UITabBarControlleron iPhone):

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

Simple usage:

简单用法:

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

Works perfect:-)

工作完美:-)

UPDATE for clean code:

更新干净的代码:

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

回答by Aneil Mallavarapu

You could also post a notification via NSNotificationCenter. This let's you deal with a number of situations where traversing the view controller hierarchy might be tricky - for example when modals are being presented, etc.

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

E.g.,

例如,

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

In each of your View Controllers:

在每个视图控制器中:

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

You could also use this approach to instrument controls which need to update when a notification is received and are used by several view controllers. In that case, handle the add/remove observer calls in the init and dealloc methods, respectively.

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

回答by Jeehut

Code

代码

Here's an approach using the great switch-case syntaxin Swift 3/4/5:

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

The basic idea is the same as in zirinisp's answer, it's just using a more Swift 3+ like syntax.

基本思想与 zirinisp 的答案相同,它只是使用更像 Swift 3+ 的语法。



Usage

用法

You probably want to create a file named UIWindowExtension.swift. Make sure it includes the import UIKitstatement, now copy the above extension code.

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

On the call side it can be either used without any specific view controller:

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

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

Or if you know your visible view controller is reachable from a specific view controller:

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

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

I hope it helps!

我希望它有帮助!

回答by nvrtd frst

I have found that iOS 8 has screwed everything up. In iOS 7 there is a new UITransitionViewon the view hierarchy whenever you have a modally presented UINavigationController. Anyway, here's my code that finds gets the topmost VC. Calling getTopMostViewControllershould return a VC that you should be able to send a message like presentViewController:animated:completion. It's purpose is to get you a VC that you can use to present a modal VC, so it will most likely stop and return at container classes like UINavigationControllerand NOT the VC contained within them. Should not be hard to adapt the code to do that too. I've tested this code in various situations in iOS 6, 7 and 8. Please let me know if you find bugs.

我发现 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;
}

回答by jungledev

Way less code than all other solutions:

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

Objective-C version:

Objective-C 版本:

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

    return topViewController;
}

Swift 2.0 version: (credit goes to Steve.B)

Swift 2.0 版本:(归功于 Steve.B)

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

Works anywhere in your app, even with modals.

可在您的应用程序中的任何位置使用,甚至可以使用模态。

回答by Bobj-C

zirinisp's Answer in Swift:

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

Usage:

用法:

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

回答by Neel Kamal

Specify title to each ViewController and then get the title of current ViewController by the code given below.

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

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

Then check it by your title like this

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

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

回答by Nicolas Manzini

Mine is better! :)

我的更好!:)

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