获取AppDelegate.m在屏幕上显示当前的UIViewController(Get the c

2019-06-17 13:19发布

当前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

}

Answer 1:

您可以使用rootViewController还当你的控制器不是UINavigationController

UIViewController *vc = self.window.rootViewController;

一旦你知道的根视图控制器,那就要看你如何建立你的用户界面,但你都不可能找到一个方式,通过控制器的层次结构进行导航。

如果你给你定义你的应用程序的方式一些更多的细节,那么我可能给一些提示。

编辑:

如果你想在最上面的视图 (不视图控制器),您可以检查

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

尽管这一观点可能是不可见的,甚至一些它的子视图的覆盖...

再次,它取决于你的UI,但是这可能帮助...



Answer 2:

我永远爱涉及的类别,因为它们是螺栓上,可以方便地重复使用的解决方案。

所以,我创建了一个UIWindow上一个类别。 现在,您可以拨打visibleViewController在一个UIWindow,这将通过向下搜索控制器层次让你可见视图控制器。 这工作如果您正在使用导航和/或标签栏控制器。 如果您有其他类型的控制器的建议,请让我知道,我可以添加它。

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


Answer 3:

在斯威夫特的简单扩展UIApplication的(甚至关心约moreNavigationController内UITabBarController在iPhone上):

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


Answer 4:

您也可以通过发布一个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的方法,去除通话观察员分别。



Answer 5:

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


Answer 6:

下面是使用在斯威夫特3的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的答案,它只是采用了更为迅捷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`
}

我希望它能帮助!



Answer 7:

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

Objective-C的版本:

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

    return topViewController;
}

雨燕2.0的版本:(归功于Steve.B)

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

任何地方工作在您的应用程序,即使情态动词。



Answer 8:

指定标题到每个视图控制器,然后通过下面给出的代码获取当前视图控制器的标题。

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

然后,通过您的标题像这样的检查

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


Answer 9:

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


Answer 10:

我的是更好! :)

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


Answer 11:

为什么不直接处理在应用程序的委托推送通知的代码? 难道是直接关系到一个看法?

您可以检查是否UIViewController的观点是目前通过检查,如果它的视图的可视window属性有值。 查看更多在这里 。



Answer 12:

关于NSNotificationCenter邮政以上(抱歉不能找出张贴在其评论...)

在一些情况下,都拿到了 - 各种各样的[NSConcreteNotification allKeys]错误。 更改此:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo

为此:

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


Answer 13:

只是除了@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。



Answer 14:

这为我工作。 我有有不同的控制器,因此以前的答案没有似乎工作许多目标。

首先你想这是你的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!")    
   }
}


Answer 15:

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



Answer 16:

雨燕2.0的版本jungledev的回答

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


Answer 17:

这是我已经尝试了尽可能最好的方式。 如果它应该帮助的人......

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

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

    return topController;
}


Answer 18:

经常检查你的构建配置如果您正在使用调试或释放您的应用程序。

重要提示:你可以不能够测试它无需调试模式下运行你的应用程序

这是我的解决办法



Answer 19:

我创建了一个类别UIApplicationvisibleViewControllers财产。 其主要思想是非常简单。 我搅和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

斯威夫特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



文章来源: Get the current displaying UIViewController on the screen in AppDelegate.m