web-dev-qa-db-ja.com

アプリデリゲートから現在のViewControllerを取得します(モーダルが可能です)

アプリデリゲートから現在のViewControllerを取得するには、アプリ用に設定したnavigationControllerプロパティを使用できることを知っています。ただし、アプリ全体の多くの場所で、モーダルナビゲーションコントローラーが表示されている可能性があります。現在のナビゲーションコントローラーは、アプリデリゲートが参照を保持しているものとは異なるため、アプリデリゲートからこれを検出する方法はありますか?

7
Mason

NSNofiticationCenterを使用することをお勧めします。

//in AppDelegate:
@interface AppDelegate()
{
    ...
    id lastViewController;
    ...
}

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCurrentViewController) name:@"CurrentViewController" object:nil];
    ...
}

- (void)handleCurrentViewController:(NSNotification *)notification {
    if([[notification userInfo] objectForKey:@"lastViewController"]) {
        lastViewController = [[notification userInfo] objectForKey:@"lastViewController"];
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{    
    NSLog(@"last view controller is %@", [(UIViewController *)lastViewController class]);
}
@end

//in every ViewController you want to detect
@implementation SomeViewController
...
- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"CurrentViewController" object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self, @"lastViewController", nil]];
}
...
@end
7
ziggear

要点 に基づいて、[[UIApplication sharedApplication] topMostViewController]を呼び出すと、アプリで最上位のView Controllerが得られるように、最上位のViewControllerを取得するためのカテゴリを作成しました。

これは、UIAlertViewUIActionSheetが廃止されてUIAlertControllerが廃止されたiOS8で特に役立ちます。これは、最上位のViewControllerに表示する必要があります。

UIViewController + TopMostViewController.h

#import <UIKit/UIKit.h>

@interface UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

@interface UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

UIViewController + TopMostViewController.m

#import "UIViewController+TopMostViewController.h"

@implementation UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController
{
    if (self.presentedViewController == nil)
    {
        return self;
    }
    else if ([self.presentedViewController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
        UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
        return [lastViewController topMostViewController];
    }

    UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;
    return [presentedViewController topMostViewController];
}

@end

#pragma mark -

@implementation UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController
{
    return [self.keyWindow.rootViewController topMostViewController];
}

@end
9
junjie

これは私のために働いた。コントローラーが異なるターゲットがたくさんあるので、以前の回答はうまくいかなかったようです。

まず、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!")    
   }
}
4
CodeOverRide

次のようにして、トップを取得してみてくださいview

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

このビューは見えないか、サブビューの一部で覆われている可能性がありますが...

UIによって異なりますが、役立つ場合があります。

3
Milo

Swiftの優れたソリューション、AppDelegateに実装

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}

目的-C

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
       UITabBarController* tabBarController = (UITabBarController*)rootViewController;
       return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
      UINavigationController* navigationController = (UINavigationController*)rootViewController;
      return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
     } else if (rootViewController.presentedViewController) {
       UIViewController* presentedViewController = rootViewController.presentedViewController;
       return [self topViewControllerWithRootViewController:presentedViewController];
     } else {
       return rootViewController;
    }
}
3
Edward Novelo

App Delegateにナビゲーションコントローラーがある場合は、 visibleViewController プロパティを使用するだけです。モーダルであっても、目に見えるコントローラーが表示されます。

1
hukir

Swiftでは、次のようにアクティブなViewControllerを取得できます:

 let navigationController = application.windows[0].rootViewController as UINavigationController

 let activeViewCont = navigationController.visibleViewController
1
jmcastel

以下のコードは非常にうまく機能します。

+(UIViewController *)findBestViewController:(UIViewController *)vc {

if (vc.presentedViewController) {

    // Return presented view controller
    return [AppDelegate findBestViewController:vc.presentedViewController];

} else if ([vc isKindOfClass:[UISplitViewController class]]) {

    // Return right hand side
    UISplitViewController* svc = (UISplitViewController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.viewControllers.lastObject];
    else
        return vc;

} else if ([vc isKindOfClass:[UINavigationController class]]) {

    // Return top view
    UINavigationController* svc = (UINavigationController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.topViewController];
    else
        return vc;

} else if ([vc isKindOfClass:[UITabBarController class]]) {

    // Return visible view
    UITabBarController* svc = (UITabBarController*) vc;
    if (svc.viewControllers.count > 0)
        return [AppDelegate findBestViewController:svc.selectedViewController];
    else
        return vc;

} else {

    // Unknown view controller type, return last child view controller
    return vc;

}

}

+(UIViewController *)currentViewController {

// Find best view controller
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
return [AppDelegate findBestViewController:viewController];

}

0
umitcel

最良の解決策は、moreNavigationControllerUITabBarControllerでも機能します。

extension UIApplication {
    class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return topViewController(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(presented)
        }

        return base
    }
}

上記の他のソリューションは、複雑なビュー階層が処理されないため、部分的にしか機能しません(タブバーコントローラーに統合されたナビゲーションコントローラー、分割ビューコントローラー、ビューコンテナー、およびアラートコントローラーが混乱する可能性があります)。

AppDelegateに現在のViewControllerの参照を保持することで、これを解決します。ビューが表示されるたびに、viewDidAppear(animated:)を利用して、アプリデリゲートで参照を設定します。

質問がObjective-Cに関するものであることは知っていますが、提供できるのはSwiftコードのみであり、両方のタイプのユーザーに役立つと確信しています。

最初に:私は物事を清潔で再利用可能に保つためのプロトコルを実装しました:

protocol UpdatableViewController {
    func updateUI()
}

2番目:AppDelegateへの参照を追加しました:

var currentViewController: UpdatableViewController?

3番目:viewDidAppear()で現在のViewControllerを設定します。

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
    appDelegate?.currentViewController = self
}

第4:

extension ViewController1: UpdatableViewController {
    func updateUI() {
        print("Implement updating here")
    }
}
0
Andrej