画面上の現在のUIViewController
は、バッジビューを設定して、APNからのプッシュ通知に応答する必要があります。しかし、どうすればapplication:didReceiveRemoteNotification
のmethodAppDelegate.m
:でUIViewController
を取得できますか?
self.window.rootViewController
を使用して、現在表示しているUIViewController
を取得しようとしました。これは、UINavigationViewController
または他の種類のView Controllerである可能性があります。そして、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
}
コントローラーがrootViewController
でない場合にもUINavigationController
を使用できます。
UIViewController *vc = self.window.rootViewController;
ルートView Controllerがわかれば、UIの構築方法によって異なりますが、コントローラー階層をナビゲートする方法を見つけることができます。
アプリの定義方法についてさらに詳しく説明する場合は、さらにヒントを示します。
編集:
最上位のview(View Controllerではない)が必要な場合は、確認できます。
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
ただし、このビューは非表示であるか、サブビューの一部で覆われている場合もあります...
繰り返しますが、それはあなたのUIに依存しますが、これは役立つかもしれません...
カテゴリがボルトオンであり、簡単に再利用できるため、カテゴリを含むソリューションが大好きです。
そこで、UIWindowにカテゴリを作成しました。これで、UIWindowでvisibleViewControllerを呼び出すことができます。これにより、コントローラー階層を下に検索することで表示ビューコントローラーを取得できます。これは、NavigationやTab Bar Controllerを使用している場合に機能します。提案する別のタイプのコントローラーがある場合はお知らせください。追加できます。
IWindow + PazLabs.h(ヘッダーファイル)
#import <UIKit/UIKit.h>
@interface UIWindow (PazLabs)
- (UIViewController *) visibleViewController;
@end
IWindow + 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
}
}
}
}
SwiftのUIApplicationの単純な拡張(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
}
完璧に動作します:-)
クリーンコードの更新:
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
}
}
NSNotificationCenterを使用して通知を投稿することもできます。これにより、View Controllerの階層をたどるのが難しい可能性のある多くの状況に対処できます。たとえば、モーダルが表示されている場合などです。
例えば。、
// 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];
}
各View Controllerで:
-(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
}
}
このアプローチを使用して、通知を受信したときに更新する必要があるコントロールをインストルメント化し、複数のView Controllerで使用することもできます。その場合は、initメソッドとdeallocメソッドでそれぞれ追加/削除オブザーバー呼び出しを処理します。
素晴らしいswitch-case構文 in Swiftを使用したアプローチを次に示します。
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のような構文を使用しているだけです。
おそらくUIWindowExtension.Swift
という名前のファイルを作成する必要があります。これにimport UIKit
ステートメントが含まれていることを確認してください。今すぐ上記の拡張コードをコピー.
呼び出し側では、次のいずれかを使用できます特定のView Controllerなし:
if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
// do whatever you want with your `visibleViewCtrl`
}
または、表示可能なView Controllerが到達可能であることがわかっている場合特定のView Controllerから:
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
などのコンテナクラスで停止し、その中に含まれる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;
}
他のすべてのソリューションよりも少ないコード:
Objective-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
}
モーダルであっても、アプリのどこでも動作します。
各ViewControllerのタイトルを指定し、以下に示すコードにより現在のViewControllerのタイトルを取得します。
-(void)viewDidUnload {
NSString *currentController = self.navigationController.visibleViewController.title;
次に、このようなタイトルで確認してください
if([currentController isEqualToString:@"myViewControllerTitle"]){
//write your code according to View controller.
}
}
swiftの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)
}
私のほうがいい! :)
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
}
}
}
アプリデリゲートでプッシュ通知コードを処理するだけではどうですか?ビューに直接関連していますか?
UIViewControllerのビューが現在表示されているかどうかを確認するには、ビューのwindow
プロパティに値があるかどうかを確認します。詳しくは こちら をご覧ください。
これは私のために働いた。コントローラーが異なる多くのターゲットがあるため、以前の回答は機能しなかったようです。
まず、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!")
}
}
@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に感謝します。
上記のNSNotificationCenter投稿について(その下にコメントを投稿する場所を見つけることができません...)
いくつかの種類の-[NSConcreteNotification allKeys]エラーが発生した場合。これを変更:
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo
これに:
-(void)didReceiveRemoteNotification:(NSNotification*)notif {
NSDictionary *dict = notif.userInfo;
}
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
を返すことです。
これは私が試した中で最良の方法です。それが誰かを助けるべきなら...
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
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.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/e6287b99011f2437ac0beb5a72a897f
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
デバッグまたはリリースでアプリを実行している場合は、常にビルド構成を確認してください。
重要な注意:アプリをデバッグモードで実行しないとテストできません
これが私の解決策でした
Jungledevの答えのSwift 2.0バージョン
func getTopViewController() -> UIViewController {
var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
while (topViewController.presentedViewController != nil) {
topViewController = topViewController.presentedViewController!
}
return topViewController
}