Obtenir le contrôleur de vue actuel de l'app delegate (modal est possible)
je sais que pour obtenir le contrôleur de vue actuel de l'app delegate, je peux utiliser la propriété navigationController
que j'ai configurée pour mon app. Cependant, il est possible que dans de nombreux endroits de mon application, un contrôleur de navigation modale ait pu être présenté. Y a-t-il un moyen de détecter cela de la part de l'app delegate, puisque le contrôleur de navigation actuel sera différent de celui auquel l'app delegate détient une référence?
10 réponses
je vous suggère D'utiliser 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
basé sur le gist ici , j'ai fait une catégorie pour obtenir le contrôleur de vue le plus haut, de sorte que l'appel [[UIApplication sharedApplication] topMostViewController]
vous donnera le contrôleur de vue le plus haut dans votre application.
ceci est particulièrement utile dans iOS 8 où UIAlertView
et UIActionSheet
ont été dépréciés en faveur de UIAlertController
, qui doit être présenté sur le contrôleur de vue le plus haut.
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
ça a marché pour moi. J'ai beaucoup d'objectifs différents contrôleurs des réponses précédentes n'a pas semblé fonctionner.
tout d'abord, vous voulez cela dans votre classe AppDelegate:
var window: UIWindow?
puis, dans votre fonction
let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
if activeController.isKindOfClass( MyViewController ) {
println("I have found my controller!")
}
}
vous pouvez essayer d'obtenir le plus haut vue en faisant:
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
bien que ce point de vue puisse être invisible ou même couvert par certains de ses subviews...
cela dépend de votre UI mais cela pourrait aider.
grande solution dans Swift, mettre en œuvre dans 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
}
Objectif-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;
}
}
si vous avez le contrôleur de navigation dans L'App Delegate, il vous suffit d'utiliser la propriété visibleViewController . Il vous donnera le visible contrôleur, même si c'est un modal.
dans swift, vous pouvez obtenir le ViewController actif comme ceci:
let navigationController = application.windows[0].rootViewController as UINavigationController
let activeViewCont = navigationController.visibleViewController
la meilleure solution, fonctionne aussi avec moreNavigationController
dans 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
}
}
ci-dessous le code fonctionne très bien.
+(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];
}
les autres solutions ci-dessus ne fonctionnent que partiellement car les hiérarchies de vues complexes ne sont pas gérées (le contrôleur de navigation intégré dans le contrôleur de la barre d'onglets, les contrôleurs de vue fractionnée, les conteneurs de vue et aussi les contrôleurs d'alerte pourraient tout gâcher).
Je résous cela en gardant une référence du contrôleur de vue actuel dans AppDelegate. Chaque fois que la vue apparaît je profite de
viewDidAppear(animated:)
et placer la référence dans le délégué app.
je sais que la question concernait L'Objectif-C, mais je ne peux fournir que du code Swift et je suis sûr qu'il sera utile aux deux types d'utilisateurs.
D'abord: j'ai mis en place un protocole pour garder les choses propres et réutilisables:
protocol UpdatableViewController {
func updateUI()
}
Second: j'ai ajouté une référence à AppDelegate:
var currentViewController: UpdatableViewController?
troisième: j'ai réglé le contrôleur de vue courant dans viewDidAppear ():
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
appDelegate?.currentViewController = self
}
quatrième:
extension ViewController1: UpdatableViewController {
func updateUI() {
print("Implement updating here")
}
}