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

屏幕上当前的 UIViewController需要通过设置一些徽章视图来响应来自 APN 的推送通知。但是我怎样才能在方法 application:didReceiveRemoteNotification: 的 AppDelegate.m中得到 UIViewController呢?

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

感谢您的帮助! 相关代码如下。

应用代表

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


}
199902 次浏览

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

UIViewController *vc = self.window.rootViewController;

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

如果你给出更多关于你定义应用程序的细节,那么我可能会给出更多的提示。

编辑:

如果您想要最上面的 风景(不是视图控制器) ,您可以检查

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

虽然这个景观可能是看不见的甚至被它的一些子视图所覆盖。

再说一次,这取决于你的用户界面,但这可能会有所帮助..。

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

您可以通过检查 UIViewController 的视图的 window属性是否有值来检查它的视图当前是否可见。详见 给你

您还可以通过 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 和 deloc 方法中的 add/delete 观察器调用。

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

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

然后像这样检查你的标题

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

关于上面的 NSNotificationCenter Post (很抱歉找不到在它下面发表评论的地方... ...)

如果有人得到了-[ NSConcreteNotificationallKeys ]排序错误,更改如下:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo

回到这里:

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

我总是喜欢解决方案,涉及类别,因为他们是螺栓上,可以很容易地重用。

所以我在 UIWindow 上创建了一个类别。您现在可以在 UIWindow 上调用 isibleViewController,这将通过向下搜索控制器层次结构来获得可见视图控制器。如果您使用的是导航栏和/或选项卡栏控制器,那么就可以实现这一点。如果你有其他类型的控制器建议,请让我知道,我可以添加它。

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

我发现 iOS8把一切都搞砸了。在 iOS7中,只要有模态呈现的 UINavigationController,视图层次结构上就会有一个新的 UITransitionView。无论如何,这是我的代码,找到获得最高风险投资。调用 getTopMostViewController应该返回一个 VC,您应该能够发送一个类似于 presentViewController:animated:completion的消息。它的目的是让你一个 VC,你可以用来显示一个模态 VC,所以它最有可能停止和返回的容器类,如 UINavigationController和不包含在他们的 VC。应该不难调整代码来做到这一点。我已经在 iOS6、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;
}

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

用法:

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

在 Swift (更关心 iPhone 上 UITabBarController中的更多导航控制器)中应用 UIApplication 的简单扩展:

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

这对我很有用,我有很多目标,它们有不同的控制器,所以以前的答案似乎不起作用。

首先,你需要在你的应用代理类中使用这个:

var window: UIWindow?

然后,在你的功能

let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
if activeController.isKindOfClass( MyViewController )  {
println("I have found my controller!")
}
}

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

比其他解决方案少得多的代码:

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

可以在应用程序的任何地方工作,甚至与模态。

Swift 2.0版的 jungledev 回答

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

这是我试过的最好的方法。如果它能帮助任何人..。

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


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


return topController;
}

我使用 visibleViewControllers属性为 UIApplication创建了一个类别。主要思想很简单。我在 UIViewController中调整了 viewDidAppearviewDidDisappear方法。在 viewDidAppear方法中,viewController 被添加到堆栈中。在 viewDidDisappear方法中,viewController 从堆栈中删除。使用 NSPointerArray代替 NSArray来存储弱 UIViewController的引用。这种方法适用于任何 viewController 层次结构。

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

密码

下面是一个使用 斯威夫特3/4/5中最好的 开关大小写语法的方法:

import UIKit


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


用法

您可能希望在其中创建一个名为 UIWindowExt.swift复制上述分机号码的文件。

在呼叫端,它可以使用 没有任何特定的视图控制器:

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

希望能有所帮助!

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

我的更好! :)

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

如果您正在使用调试或发行版运行应用程序,请始终检查您的构建配置。

重要提示: 如果不在调试模式下运行应用程序,就无法对其进行测试

这就是我的解决办法

在 Swift 中,只需将这一行代码添加到 BaseViewController 记录显示的视图 控制器

override public func viewDidLoad() {


super.viewDidLoad()


print("Current ViewController 🙋🏻‍♂️  #\(UIApplication.getTopViewController()?.classForCoder ?? self.classForCoder)")

}