在AppDelegate.m的屏幕上获取当前显示的UIViewController
屏幕上的当前UIViewController
需要响应来自APN的推送通知,通过设置一些徽章视图。 但是我怎么能得到方法application:didReceiveRemoteNotification
的UIViewController
application:didReceiveRemoteNotification
:的AppDelegate.m
?
我尝试使用self.window.rootViewController
获取当前显示的UIViewController
,它可能是一个UINavigationViewController
或其他types的视图控制器。 我发现UINavigationViewController
的visibleViewController
属性可以用来获取屏幕上的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 }
当你的控制器不是UINavigationController
时,你也可以使用rootViewController
:
UIViewController *vc = self.window.rootViewController;
一旦你知道了根视图控制器,那么这取决于你如何构build你的UI,但你可能find一种方法来浏览控制器层次结构。
如果您提供关于您定义应用的方式的更多详细信息,那么我可能会提供更多的提示。
编辑:
如果你想要最顶层的视图 (不是视图控制器),你可以检查
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
尽pipe这种观点可能是隐形的,甚至可以被某些子视图所覆盖。
再次,这取决于你的用户界面,但这可能有帮助…
我总是喜欢涉及类别的解决scheme,因为它们是可以重复使用的。
所以我在UIWindow上创build了一个类。 你现在可以在UIWindow上调用visibleViewController,这将通过search控制器层次结构来获得可见的视图控制器。 如果您正在使用导航和/或标签栏控制器,这将起作用。 如果你有其他types的控制器build议,请让我知道,我可以添加它。
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 } } } }
您也可以通过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方法中处理添加/删除观察者调用。
UIApplication在Swift中的简单扩展(甚至关心iPhone上的UITabBarController
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 }
完美的作品:-)
我发现iOS 8已经搞砸了。 在iOS 7中,只要有模态地呈现的UINavigationController
就在视图层次结构上有一个新的UITransitionView
。 无论如何,这是我的代码find最顶级的VC。 调用getTopMostViewController
应该返回一个VC,你应该能够发送一个消息,如presentViewController:animated:completion
。 它的目的是让你一个VC,你可以用它来呈现一个模态的VC,所以它很可能会停止并返回像UINavigationController
这样的容器类而不是它们中包含的VC。 也不应该很难适应代码来做到这一点。 我已经在iOS 6,7和8的各种情况下testing了这个代码。请让我知道,如果你发现错误。
+ (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; }
less于所有其他解决scheme的代码:
Objective-C版本:
- (UIViewController *)getTopViewController { UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController; return topViewController; }
Swift 2.0版本:(信贷史蒂夫.B)
func getTopViewController() -> UIViewController { var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController! while (topViewController.presentedViewController != nil) { topViewController = topViewController.presentedViewController! } return topViewController }
即使使用模式,也可以在应用中的任何位置使用。
码
下面是在Swift 3中使用伟大的开关语法的一种方法:
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的语法。
用法
您可能需要创build一个名为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` }
我希望它有帮助!
为每个ViewController指定标题,然后按照下面的代码获取当前ViewController的标题。
-(void)viewDidUnload { NSString *currentController = self.navigationController.visibleViewController.title;
然后按照你的标题来检查
if([currentController isEqualToString:@"myViewControllerTitle"]){ //write your code according to View 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) }
为什么不在应用程序委托中处理推送通知代码? 它是否与视图直接相关?
你可以通过检查视图的window
属性是否有值来检查当前UIViewController的视图是否可见。 在这里看到更多。
关于NSNotificationCenter上面的post(抱歉找不到在哪里发表评论…)
万一有人得到 – [NSConcreteNotification allKeys]错误的种类。 改变这个:
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
对此:
-(void)didReceiveRemoteNotification:(NSNotification*)notif { NSDictionary *dict = notif.userInfo; }
除了@zirinisp答案。
创build一个文件,将其命名为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。
这对我有效。 我有很多目标有不同的控制器,所以以前的答案似乎没有工作。
首先你需要在你的AppDelegate类中:
var window: UIWindow?
然后,在你的function
let navigationController = window?.rootViewController as? UINavigationController if let activeController = navigationController!.visibleViewController { if activeController.isKindOfClass( MyViewController ) { println("I have found my controller!") } }
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; }
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
。
我使用visibleViewControllers
属性为UIApplication
创build了一个类别。 主要想法很简单。 我在UIViewController
viewDidDisappear
viewDidAppear
和viewDidDisappear
方法。 在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
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
我的更好! 🙂
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 } } }