Est-il possible de déterminer si ViewController est présenté comme modal?

117

Est-il possible de vérifier à l'intérieur de la classe ViewController qu'il est présenté comme un contrôleur de vue modale?

tiède
la source

Réponses:

96

Depuis modalViewControllerest obsolète dans iOS 6, voici une version qui fonctionne pour iOS 5+ et qui se compile sans avertissement.

Objectif c:

- (BOOL)isModal {
    return self.presentingViewController.presentedViewController == self
      || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
      || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
}

Rapide:

var isModal: Bool {
    return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController)
        || self.tabBarController?.presentingViewController is UITabBarController
}

Pointe du chapeau à la réponse de Felipe.

Gabriele Petronella
la source
2
bonne prise, j'ai juste dû l'utiliser à nouveau après un long moment et j'ai remarqué que la dépréciation s'était produite ... J'ai modifié ma réponse pour que les gens commencent à chercher ici le bon code lors de l'utilisation d'iOS 6+, merci
Felipe Sabino
10
Ne fonctionne pas si le contrôleur de vue parent est un modal sur lequel notre contrôleur de vue est poussé.
sens-questions
2
Il y a un bug, il faut vérifier si les deux côtés sont nuls, car nil == nilretourne YES, et ce n'est pas le résultat que l'on veut.
CocoaBob
1
@GabrielePetronella Cela vous dérange si je mets à jour la réponse pour inclure également une implémentation Swift de la méthode?
Michael Waterfall
1
@MichaelWaterfall qui serait grandement apprécié, merci
Gabriele Petronella
77

Si vous recherchez iOS 6+, cette réponse est obsolète et vous devriez vérifier la réponse de Gabriele Petronella


Il n'y a pas de moyen efficace de le faire, en tant que propriété ou méthode native d'UIKit. Ce que vous pouvez faire est de vérifier plusieurs aspects de votre contrôleur pour vous assurer qu'il est présenté comme modal.

Donc, pour vérifier si le contrôleur actuel (représenté comme selfdans le code ci-dessous) est présenté de manière modale ou non, j'ai la fonction ci-dessous soit dans une UIViewControllercatégorie, soit (si votre projet n'a pas besoin d'utiliser d'autres contrôleurs UIKit, comme UITableViewControllerpar exemple) dans un contrôleur de base dont mes autres contrôleurs héritent

-(BOOL)isModal {

     BOOL isModal = ((self.parentViewController && self.parentViewController.modalViewController == self) || 
            //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
            ( self.navigationController && self.navigationController.parentViewController && self.navigationController.parentViewController.modalViewController == self.navigationController) || 
            //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
            [[[self tabBarController] parentViewController] isKindOfClass:[UITabBarController class]]);

    //iOS 5+
    if (!isModal && [self respondsToSelector:@selector(presentingViewController)]) {

        isModal = ((self.presentingViewController && self.presentingViewController.modalViewController == self) || 
             //or if I have a navigation controller, check if its parent modal view controller is self navigation controller
             (self.navigationController && self.navigationController.presentingViewController && self.navigationController.presentingViewController.modalViewController == self.navigationController) || 
             //or if the parent of my UITabBarController is also a UITabBarController class, then there is no way to do that, except by using a modal presentation
             [[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]]);

    }

    return isModal;        

}

EDIT: J'ai ajouté la dernière vérification pour voir si un UITabBarController est utilisé, et vous présentez un autre UITabBarController comme modal.

EDIT 2: ajout de la vérification iOS 5+, où UIViewControllerne répond parentViewControllerplus, mais à la presentingViewControllerplace.

EDIT 3: J'ai créé un résumé pour cela au cas où https://gist.github.com/3174081

Felipe Sabino
la source
Gardez à l'esprit que la modalViewControllerpropriété est obsolète à partir d'iOS 6. La documentation suggère de l'utiliser à la presentedViewControllerplace.
Bart Jacobs
@BartJacobs bon point! Je n'ai pas regardé cette réponse après la sortie d'iOS6, elle n'est donc peut-être pas à jour. J'essaierai de faire quelques tests plus tard dans la semaine pour le mettre à jour, tks!
Felipe Sabino
NSLog(@"%@", self.navigationController.parentViewController)estampes (null)- pourriez-vous expliquer pourquoi? Mon ViewController est connecté au contrôleur de vue modale via navController dans le storyboard.
Roman
@oyatek pouvez-vous utiliser pastebin ou quelque chose de similaire et afficher du code?
Felipe Sabino
@Feilpe J'ai trouvé le problème - .parentViewControllerest obsolète, .presentingViewControllerdoit être utilisé à la place.
Roman
35

Dans iOS5 +, comme vous pouvez le voir dans UIViewController Class Reference , vous pouvez l'obtenir à partir de la propriété "presentationViewController".

presentantViewController Le contrôleur de vue qui a présenté ce contrôleur de vue. (lecture seulement)

@property (non atomique, en lecture seule) UIViewController * présentant ViewController
Discussion

Si le contrôleur de vue qui a reçu ce message est présenté par un autre contrôleur de vue, cette propriété contient le contrôleur de vue qui le présente. Si le contrôleur de vue n'est pas présenté, mais que l'un de ses ancêtres est présenté, cette propriété contient le contrôleur de vue présentant l'ancêtre le plus proche. Si ni le contrôleur de vue ni aucun de ses ancêtres ne sont présentés, cette propriété est nulle.

Disponibilité
Disponible dans iOS 5.0 et versions ultérieures.
Déclaré dans
UIViewController.h

Raj
la source
3
Fonctionne parfaitement, utilisez if (self.presentingViewController) {// Ceci est un viewContoller modal} else {// Ceci est un ViewController normal}
mashdup
2
IMHO, c'est la seule bonne réponse ici. Vérifiez simplement la présence d'un fichier presentingViewController. Il fonctionnera également dans les contrôleurs de vue de conteneur, car il traverse automatiquement les ancêtres.
Daniel Rinser
17

Si ce n'est pas le cas, vous pouvez définir une propriété pour this ( presentedAsModal) dans votre sous-classe UIViewController et la définir sur YESavant de présenter ViewController en tant que vue modale.

childVC.presentedAsModal = YES;
[parentVC presentModalViewController:childVC animated:YES];

Vous pouvez vérifier cette valeur dans votre viewWillAppearremplacement.

Je crois qu'il n'y a pas de propriété officielle qui indique comment la vue est présentée, mais rien ne vous empêche de créer la vôtre.

hpique
la source
Bien et c'est ce que j'ai fait mais je cherchais une autre solution intéressante. Merci.
lukewar
cette solution ne fonctionne pas si vous présentez un UINavigationControllercomme modal ... sauf si vous créez un contrôleur de navigation personnalisé juste pour ajouter cette propriété. Et après cela, à l'intérieur des contrôleurs, vous devrez continuer à lancer self.navigationControllerdans cette classe personnalisée à chaque fois que vous devez vérifier si le contrôleur est présenté comme modal
Felipe Sabino
8

La réponse de Petronella ne fonctionne pas si self.navigationController est présenté modalement mais que self n'est pas égal à self.navigationController.viewControllers [0], dans ce cas self est poussé.

Voici comment vous pouvez résoudre le problème.

return self.presentingViewController.presentedViewController == self
            || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController && self == self.navigationController.viewControllers[0])
            || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];

Et dans Swift:

return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController && self.navigationController?.viewControllers[0] == self)
        || self.tabBarController?.presentingViewController is UITabBarController
Semih Cihan
la source
6

Cela devrait fonctionner.

if(self.parentViewController.modalViewController == self)…
kubi
la source
Malheureusement ça ne fonctionne pas. C'était mon premier essai. Mais retourné modalViewController ins nil :(.
lukewar
Si vous obtenez simplement 'self.parentViewController', cela renvoie-t-il l'objet parent correct?
kubi
4
Le problème peut être que votre sous-classe UIViewController se trouve dans un UINavigationController ou un UITabBarController (ou les deux), auquel cas vous devrez peut-être creuser un peu plus dans la hiérarchie des vues pour trouver le parent qui a été présenté comme un contrôleur de vue modale.
hpique
@hgpc J'avais besoin de ce chck dans mon projet, je viens donc d'ajouter une réponse pour vérifier les deux UINavigationControlleret les UITabBarControllercas. Cela fonctionne plutôt bien jusqu'à présent
Felipe Sabino
4

Meilleur moyen de vérifier

 if (self.navigationController.presentingViewController) {
         NSLog(@"Model Present");
    }
Sunny Shah
la source
2

Si vous n'avez pas besoin de faire la distinction entre les vues modales plein écran et les vues non modales, ce qui est le cas dans mon projet (je traitais d'un problème qui ne se produit qu'avec les feuilles de formulaire et les feuilles de page), vous pouvez utiliser le modalPresentationStyle propriété de UIViewController:

switch (self.modalPresentationStyle) {
    case 0: NSLog(@"full screen, or not modal"); break;
    case 1: NSLog(@"page sheet"); break;
    case 2: NSLog(@"form sheet"); break;
}
arlomedia
la source
2

Dans Swift :

func isUIViewControllerPresentedAsModal() -> Bool {
    if((self.presentingViewController) != nil) {
        return true
    }

    if(self.presentingViewController?.presentedViewController == self) {
        return true
    }

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
        return true
    }

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
        return true
    }

    return false
}
Roi-sorcier
la source
Il y a un problème avec ce cas d'utilisation. Si je suis dans un contrôleur de vue racine d'un UINavigationController, il renvoie toujours vrai sans aucune présentation modale.
mariusLAN
1
La première instruction if couvre tout ce qui se trouve dans la seconde instruction if, rendant la seconde instruction redondante. Je ne sais pas quelle est l'intention ici.
isoiphone le
1

Dans mon projet, j'ai un contrôleur de vue (détail) qui peut être présenté de manière modale (lors de l'ajout d'un nouvel élément) ou avec push (lors de l'édition d'un élément existant) par le contrôleur de vue maître. Lorsque l'utilisateur appuie sur [Terminé], le contrôleur de vue détaillée appelle la méthode du contrôleur de vue principale pour notifier qu'il est prêt à être fermé. Le maître doit déterminer comment le détail est présenté afin de savoir comment le fermer. Voici comment je fais ceci:

UIViewController *vc = self.navigationController.viewControllers.lastObject;
if (vc == self) {
    [self dismissViewControllerAnimated:YES completion:NULL];
} else {
    [self.navigationController popViewControllerAnimated:YES];
}
Olex
la source
0

Un hack comme celui-ci pourrait fonctionner.

UIViewController* child = self;
UIViewController* parent = child.parentViewController;
while (parent && parent.modalViewController != child) {
    child = parent;
    parent = child.parentViewController;
}
if (parent) {
    // A view controller in the hierarchy was presented as a modal view controller
}

Cependant, je pense que ma réponse précédente est une solution plus propre.

hpique
la source
0

Ce qui a fonctionné pour moi est le suivant:

// this is the trick: set parent view controller as application's window root view controller
UIApplication.sharedApplication.delegate.window.rootViewController = viewController;

// assert no modal view is presented
XCTAssertNil(viewController.presentedViewController);

// simulate button tap which shows modal view controller
[viewController.deleteButton sendActionsForControlEvents:UIControlEventTouchUpInside];

// assert that modal view controller is presented
XCTAssertEqualObjects(viewController.presentedViewController.class, MyModalViewController.class);

Pour autant que je l'ai testé, cela fonctionne pour iOS7 et iOS8. Je n'ai pas essayé sur iOS6 cependant.

mixtly87
la source
0

J'ai regardé un peu partout pour trouver la bonne réponse à cette question, et je n'en ai trouvé aucune couvrant tous les scénarios possibles. J'ai écrit ces quelques lignes de code qui semblent faire le travail. Vous pouvez trouver quelques commentaires en ligne pour savoir ce qui a été vérifié.

- (BOOL)isModal {
    BOOL modal = NO;
    if ([self presentingViewController]) { //Some view Controller is presenting the current stack
        UIViewController *presented = [[self presentingViewController] presentedViewController]; // What's been presented
        if ([presented respondsToSelector:@selector(viewControllers)]) { // There's a stack
            NSArray *viewControllers = [presented performSelector:@selector(viewControllers)];
            modal = [viewControllers firstObject] == self; // Current VC is presented modally if it's the first in the stack
        }
        else {
            modal = presented == self; // Don't think this is actually needed. set modal = YES should do the job tho.
        }
    }
    return modal;
}

J'espère que cette aide.

DennyLou
la source
0

Voici ma version modifiée de @ GabrielePetronella isModal, qui fonctionne avec les contrôleurs de vue contenus en ce sens qu'elle remonte d'abord la hiérarchie parentViewController. Également extrait le code en plusieurs lignes afin que ce soit clair ce qu'il fait.

var isModal: Bool {
    // If we are a child view controller, we need to check our parent's presentation
    // rather than our own.  So walk up the chain until we don't see any parentViewControllers
    var potentiallyPresentedViewController : UIViewController = self
    while (potentiallyPresentedViewController.parentViewController != nil) {
        potentiallyPresentedViewController = potentiallyPresentedViewController.parentViewController!
    }

    if self.presentingViewController?.presentedViewController == potentiallyPresentedViewController {
        return true
    }

    if let navigationController = potentiallyPresentedViewController.navigationController {
        if navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        }
    }

    return potentiallyPresentedViewController.tabBarController?.presentingViewController is UITabBarController
}
Ryan
la source