ios 从应用程序委托获取当前视图控制器(模态是可能的)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20485585/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:42:21  来源:igfitidea点击:

Get current view controller from the app delegate (modal is possible)

iosobjective-cdelegatesappdelegate

提问by Mason

I know that to get the current view controller from the app delegate, I can use the navigationControllerproperty I have set up for my app. However, it's possible in many places throughout my app that a modal navigation controller could have been presented. Is there any way to detect this from the app delegate, since the current navigation controller will be different from the one to which the app delegate holds a reference?

我知道要从应用程序委托获取当前视图控制器,我可以使用navigationController我为我的应用程序设置的属性。但是,在我的应用程序中的许多地方都可能出现模态导航控制器。有什么方法可以从应用程序委托中检测到这一点,因为当前导航控制器将与应用程序委托持有引用的导航控制器不同?

采纳答案by ziggear

I suggest you use NSNofiticationCenter.

我建议你使用 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

回答by junjie

Based on the gist here, I made a category to obtain the top most view controller, such that calling [[UIApplication sharedApplication] topMostViewController]will give you the top most view controller in your app.

基于这里要点,我创建了一个类别来获取最顶层的视图控制器,这样调用[[UIApplication sharedApplication] topMostViewController]就会为您提供应用程序中最顶层的视图控制器。

This is especially useful in iOS 8 where UIAlertViewand UIActionSheethave been deprecated in favor of UIAlertController, which needs to be presented on the top most view controller.

这在 iOS 8 中特别有用,在 iOS 8 中UIAlertViewUIActionSheet已被弃用而支持UIAlertController,它需要呈现在最顶部的视图控制器上。

UIViewController+TopMostViewController.h

UIViewController+TopMostViewController.h

#import <UIKit/UIKit.h>

@interface UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

@interface UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

UIViewController+TopMostViewController.m

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

回答by Edward Novelo

Great solution in Swift, implement in AppDelegate

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
}

Objective - C

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

回答by CodeOverRide

This worked for me. I have many targets that have different controllers so previous answers didn't seemed to work.

这对我有用。我有许多具有不同控制器的目标,因此以前的答案似乎不起作用。

first you want this inside your AppDelegate class:

首先,您需要在 AppDelegate 类中使用它:

var window: UIWindow?

then, in your function

然后,在你的函数中

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

回答by Milo

You could try getting the top most viewby doing:

您可以尝试通过执行以下操作来获得最高视图

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

although this view might be invisible or even covered by some of its subviews...

虽然这个视图可能是不可见的,甚至被它的一些子视图覆盖......

It depends on your UI but it might help.

这取决于您的用户界面,但它可能会有所帮助。

回答by hukir

If you have the navigation controller in the App Delegate, just use the visibleViewControllerproperty. It will give you the visible controller, even if it's a modal.

如果您在 App Delegate 中有导航控制器,只需使用visibleViewController属性。它会给你可见的控制器,即使它是一个模态。

回答by jmcastel

In swift you can get the active ViewController like this :

在 swift 中,您可以像这样获得活动的 ViewController:

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

 let activeViewCont = navigationController.visibleViewController

回答by Andrej

Other solutions above work only partially as complex view hierarchies are not handled (navigation controller integrated in tab bar controller, split view controllers, view containers and also alert controllers could mess things up).

由于未处理复杂的视图层次结构(集成在标签栏控制器中的导航控制器、拆分视图控制器、视图容器以及警报控制器可能会将事情搞砸),上述其他解决方案仅部分起作用。

I solve this by keeping a reference of the current view controller in AppDelegate. Every time the view appears I take advantage of viewDidAppear(animated:)and set the reference in the app delegate.

我通过在 AppDelegate 中保留当前视图控制器的引用来解决这个问题。每次出现视图时,我都会利用 viewDidAppear(animated:)并在应用程序委托中设置引用。

I know the question was about Objective-C, but I can provide only Swift code and I'm sure it will be useful to both types of users.

我知道这个问题是关于 Objective-C 的,但我只能提供 Swift 代码,我相信它对两种类型的用户都有用。

First: I've implemented a protocol to keep things clean and reusable:

第一:我已经实现了一个协议来保持干净和可重用:

protocol UpdatableViewController {
    func updateUI()
}

Second: I've added a reference to AppDelegate:

第二:我添加了对 AppDelegate 的引用:

var currentViewController: UpdatableViewController?

Third: I set the current view controller in viewDidAppear():

第三:我在 viewDidAppear() 中设置当前视图控制器:

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

Fourth:

第四:

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

回答by Bart?omiej Semańczyk

The best solution, works also with moreNavigationControllerin UITabBarController:

最好的解决方案,也适用于moreNavigationControllerin UITabBarController

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

回答by umitcel

Below code works very well.

下面的代码工作得很好。

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

+(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 {

+(UIViewController*) currentViewController {

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

}

}