Получить текущее отображение UIViewController на экране в AppDelegate.м
текущего UIViewController
на экране нужно ответить на push-уведомления от APNs, установив некоторые виды значков. Но как я мог получить UIViewController
методомapplication:didReceiveRemoteNotification
: of AppDelegate.m
?
Я пробовал использовать self.window.rootViewController
чтобы получить текущее отображение UIViewController
, может быть UINavigationViewController
или какой-то другой вид контроллера вид. И я узнаю, что visibleViewController
собственность UINavigationViewController
можно использовать для получения UIViewController
на экране. Но что я могу сделать, если это не UINavigationViewController
?
любая помощь приветствуется! Соответствующий код выглядит следующим образом.
AppDelegate.м
...
- (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.м
- (void)handleThePushNotification:(NSDictionary *)userInfo{
//set some badge view here
}
19 ответов:
можно использовать
rootViewController
также, Когда ваш контроллер не являетсяUINavigationController
:UIViewController *vc = self.window.rootViewController;
как только вы узнаете контроллер корневого представления, это зависит от того, как вы построили свой пользовательский интерфейс, но вы можете найти способ навигации по иерархии контроллеров.
если вы дадите более подробную информацию о том, как вы определили свое приложение, то я мог бы дать еще несколько подсказок.
EDIT:
если вы хотите, чтобы верхний посмотреть (не смотреть контроллер), вы можете проверить
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
хотя этот вид может быть невидимым или даже покрыт некоторыми из его подвидов...
опять же, это зависит от вашего пользовательского интерфейса, но это может помочь...
Я всегда люблю решения, которые включают категории, как они болт на и могут быть легко использованы повторно.
поэтому я создал категорию на UIWindow. Теперь вы можете вызвать visibleViewController на UIWindow, и это даст вам контроллер видимого вида, выполнив поиск по иерархии контроллера. Это работает, если вы используете навигацию и/или контроллер панели вкладок. Если у вас есть другой тип контроллера, чтобы предложить, пожалуйста, дайте мне знать, и я могу добавить оно.
UIWindow+PazLabs.H (заголовочный файл)
#import <UIKit/UIKit.h> @interface UIWindow (PazLabs) - (UIViewController *) visibleViewController; @end
UIWindow+PazLabs.м (выполнения файл)
#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 } }
вы также можете использовать этот подход к элементам управления приборами, которые необходимо обновить при получении уведомления и используются несколькими контроллерами вида. В этом случае обработайте вызовы add/remove observer в методах init и dealloc соответственно.
простое расширение для UIApplication в Swift (заботится даже о 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 }
работает идеально:-)
код
вот подход с использованием великого синтаксис switch-case на 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, это просто использование более быстрого синтаксиса 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` }
Я надеюсь, что это помогает!
я обнаружил, что iOS 8 все испортил. В iOS 7 появилась новая
UITransitionView
в иерархии представлений всякий раз, когда у вас есть модально представленныйUINavigationController
. В любом случае, вот мой код, который находит получает самый верхний VC. ЗвонюgetTopMostViewController
должен вернуть VC, что вы должны быть в состоянии отправить сообщение, какpresentViewController:animated:completion
. Его цель-получить VC, который вы можете использовать для представления модального VC, поэтому он, скорее всего, остановится и вернется в классы контейнеров, такие какUINavigationController
а не ВК, содержащийся в них. Не должно быть трудно адаптировать код, чтобы сделать это. Я тестировал этот код в различных ситуациях в 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; }
меньше кода, чем все другие решения:
версия Objective-C:
- (UIViewController *)getTopViewController { UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController; return topViewController; }
Swift 2.0 версия: (кредит идет к Стиву.Б)
func getTopViewController() -> UIViewController { var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController! while (topViewController.presentedViewController != nil) { topViewController = topViewController.presentedViewController! } return topViewController }
работает в любом месте в вашем приложении, даже с глагола.
укажите название для каждого ViewController, а затем получите название текущего ViewController с помощью кода, приведенного ниже.
-(void)viewDidUnload { NSString *currentController = self.navigationController.visibleViewController.title;
затем проверьте его по названию, как это
if([currentController isEqualToString:@"myViewControllerTitle"]){ //write your code according to View controller. } }
ответ зириниспа в 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) }
почему бы просто не обработать код уведомления толчка в делегате приложения? Это напрямую связано с целью?
вы можете проверить, является ли представление UIViewController в настоящее время видимым, проверив, является ли оно
window
свойство имеет значение. Смотрите подробнее здесь.
относительно сообщения NSNotificationCenter выше (извините, не могу узнать, где разместить комментарий под ним...)
в случае, если некоторые получали -[NSConcreteNotification allKeys] ошибка рода. Измените это:
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
для этого:
-(void)didReceiveRemoteNotification:(NSNotification*)notif { NSDictionary *dict = notif.userInfo; }
просто дополнение к ответу @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.
Мой лучше! :)
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 } } }
это работает для меня. У меня есть много целей, которые имеют разные контроллеры, поэтому предыдущие ответы, казалось, не работали.
сначала вы хотите, чтобы это было внутри вашего класса 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!") } }
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
вернет aUIAlertController
.
Swift 2.0 версия ответа юнгледева
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; }
всегда проверяйте конфигурацию сборки, если вы используете приложение с debug или release.
важное примечание: вы не можете быть в состоянии проверить его без запуска приложения в режиме отладки
Это было мое решение
Я создал категорию для
UIApplication
СvisibleViewControllers
собственность. Основная идея довольно проста. Я выпилviewDidAppear
иviewDidDisappear
методы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.м
#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.Свифт
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