Comment vérifier si un contrôleur de vue est présenté de manière modale ou poussé sur une pile de navigation?

126

Comment puis-je, dans mon code de contrôleur de vue, faire la différence entre:

  • présenté modalement
  • poussé sur la pile de navigation

Les deux presentingViewControlleret isMovingToParentViewControllersont YESdans les deux cas, donc ne sont pas très utiles.

Ce qui complique les choses, c'est que mon contrôleur de vue parent est parfois modal, sur lequel le contrôleur de vue à vérifier est poussé.

Il s'avère que mon problème est que j'intègre mon HtmlViewControllerdans un UINavigationControllerqui est ensuite présenté. C'est pourquoi mes propres tentatives et les bonnes réponses ci-dessous n'ont pas fonctionné.

HtmlViewController*     termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary];
UINavigationController* modalViewController;

modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController];
modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:modalViewController
                   animated:YES
                 completion:nil];

Je suppose que je ferais mieux de dire à mon contrôleur de vue quand il est modal, au lieu d'essayer de déterminer.

le sens compte
la source

Réponses:

125

Prenez avec un grain de sel, n'a pas testé.

- (BOOL)isModal {
     if([self presentingViewController])
         return YES;
     if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
         return YES;
     if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
         return YES;

    return NO;
 }
ColdLogic
la source
12
J'ai trouvé cela dans un autre article SO. Mais, ne fonctionne pas si le parent du contrôleur de vue poussé est un modal; quelle est la situation que je rencontre.
sens-questions
2
Comme je l'ai écrit, presentingViewControllerc'est toujours YESdans mon cas; n'aide pas.
sens-questions
3
presentingViewControllerretourne YESpour VC poussé, quand il y a un UITabBarControllerêtre défini en tant que racine. Donc, ne convient pas dans mon cas.
Yevhen Dubinin
5
Cela ne fonctionne pas si vous présentez un contrôleur de vue, puis il en pousse un autre.
Lee
3
"Cela ne fonctionne pas si vous présentez un contrôleur de vue, puis il en pousse un autre" Ce n'est pas l'intention de cela, le contrôleur de vue poussé n'est pas présenté.
Colin Swelin
87

Dans Swift :

Ajoutez un indicateur pour tester s'il s'agit d'un modal par le type de classe:

// MARK: - UIViewController implementation

extension UIViewController {

    var isModal: Bool {

        let presentingIsModal = presentingViewController != nil
        let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
        let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController

        return presentingIsModal || presentingIsNavigation || presentingIsTabBar
    }
}
Roi-sorcier
la source
4
Devrait être mieux dans un var, commevar isModal: Bool {}
malinois
1
@malinois est changé
YannSteph
Que fait le dernier falseparamètre de l' returninstruction?
damjandd
vous devez changer pour laisser presenterIsNavigation = navigationController? .presentingViewController? .presentedViewController == navigationController && navigationController! = nil
famfamfam
78

Vous oublié un méthode: isBeingPresented.

isBeingPresented est vrai lorsque le contrôleur de vue est présenté et faux lorsqu'il est poussé.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if ([self isBeingPresented]) {
        // being presented
    } else if ([self isMovingToParentViewController]) {
        // being pushed
    } else {
        // simply showing again because another VC was dismissed
    }
}
rmaddy
la source
2
J'ai essayé ça aussi avant de poster, et ça ne marche pas, isBeingPresentedc'est NO. Mais je vois la raison maintenant, j'intègre mon contrôleur de vue présenté dans un UINavigationController, et c'est celui que je pousse.
sens-questions
1
Vous ne pouvez pas pousser un contrôleur de navigation. Peut-être vouliez-vous dire que vous présentiez le contrôleur de navigation.
rmaddy
3
@jowie À utiliser p, pas polors de l'impression d'une valeur primitive. poest pour l'impression d'objets.
rmaddy du
37
Documentation pour isBeingPresented- Cette méthode renvoie YES uniquement lorsqu'elle est appelée depuis l'intérieur des méthodes viewWillAppear: et viewDidAppear:.
funct7
4
@Terrence Il semble que la dernière documentation ne montre pas cette information, mais elle était là. Le isBeingPresented, isBeingDismissed, isMovingFromParentViewControlleret isMovingToParentViewControllerne sont valables que dans les 4 view[Will|Did][Disa|A]ppearméthodes.
rmaddy
29

Swift 5
Voici une solution qui résout le problème mentionné avec les réponses précédentes, lorsque les isModal()retours truesi poussés UIViewControllersont dans une UINavigationControllerpile présentée .

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if navigationController?.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}

Cela fonctionne pour moi jusqu'à présent. Si quelques optimisations, merci de partager.

Jonauz
la source
Pourquoi avez-vous besoin de vérifier tabBarController?.presentingViewController is UITabBarController ? Est-ce important si c'est presentingViewControlleraussi un UITabBarController?
Hlung le
Et si navigationController est nul, isModalretournera true. Est-ce voulu?
Hlung le
28

self.navigationController! = nil signifierait qu'il est dans une pile de navigation.

Afin de gérer le cas où le contrôleur de vue actuel est poussé alors que le contrôleur de navigation est présenté de manière modale, j'ai ajouté quelques lignes de code pour vérifier si le contrôleur de vue actuel est le contrôleur racine dans la pile de navigation.

extension UIViewController {
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            return true
        } else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController {
            return true
        } else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController {
            return true
        } else {
            return false
        }
    }
}
Jibeex
la source
Eh bien en général, lorsque vous présentez modalement, vous placez le viewController sur un navigationController et vous le présentez. Si tel est le cas, votre déclaration serait erronée, mais sur le code, ce cas est traité. Veuillez améliorer votre réponse :)
E-Riddie
bon travail qui traite tous les cas d'utilisation. place pour un peu de refactoring probablement mais toujours upvote !!
Jean Raymond Daher
12

Swift 4

var isModal: Bool {
    return presentingViewController != nil ||
           navigationController?.presentingViewController?.presentedViewController === navigationController ||
           tabBarController?.presentingViewController is UITabBarController
}
Charlton Provatas
la source
Swift 4.2 / iOS 12. Fonctionne toujours bien, mais sachez que navigationController? .PresentingViewController? .PresentedViewController === navigationController évaluera à true si les deux sont nil (par exemple, si vous l'appelez sur un contrôleur de vue qui n'a pas encore été présenté).
Eli Burke
7

Swift 5. Propre et simple.

if navigationController.presentingViewController != nil {
    // Navigation controller is being presented modally
}
Kirill Kudaev
la source
1
cela a fait l'affaire pour moi
Radu Ursache
3

Comme beaucoup de gens ici le suggèrent, que les méthodes de «vérification» ne fonctionnent pas bien dans tous les cas, dans mon projet, j'ai trouvé une solution pour gérer cela manuellement. Le fait est que nous gérons généralement nous-mêmes la présentation - ce n'est pas ce qui se passe dans les coulisses et nous devons introspecter.

DEViewController.h fichier:

#import <UIKit/UIKit.h>

// it is a base class for all view controllers within a project
@interface DEViewController : UIViewController 

// specify a way viewcontroller, is presented  by another viewcontroller
// the presented view controller should manually assign the value to it
typedef NS_ENUM(NSUInteger, SSViewControllerPresentationMethod) {
    SSViewControllerPresentationMethodUnspecified = 0,
    SSViewControllerPresentationMethodPush,
    SSViewControllerPresentationMethodModal,
};
@property (nonatomic) SSViewControllerPresentationMethod viewControllerPresentationMethod;

// other properties/methods...
@end

Les présentations pourraient maintenant être gérées de cette façon:

poussé sur la pile de navigation:

// DETestViewController inherits from DEViewController
DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodPush;
[self.navigationController pushViewController:vc animated:YES];

présenté modalement avec navigation:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
UINavigationController *nav = [[UINavigationController alloc]
                               initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];

présenté modalement:

DETestViewController *vc = [DETestViewController new];
vc.viewControllerPresentationMethod = SSViewControllerPresentationMethodModal;
[self presentViewController:vc animated:YES completion:nil];

En outre, dans DEViewControllernous pourrions ajouter une solution de secours à "vérifier" si la propriété susmentionnée est égale à SSViewControllerPresentationMethodUnspecified:

- (BOOL)isViewControllerPushed
{
    if (self.viewControllerPresentationMethod != SSViewControllerPresentationMethodUnspecified) {
        return (BOOL)(self.viewControllerPresentationMethod == SSViewControllerPresentationMethodPush);
    }

    else {
        // fallback to default determination method
        return (BOOL)self.navigationController.viewControllers.count > 1;
    }
}
Yevhen Dubinin
la source
3

En supposant que tous les viewControllers que vous présentez de manière modale sont enveloppés dans un nouveau navigationController (ce que vous devriez toujours faire de toute façon), vous pouvez ajouter cette propriété à votre VC.

private var wasPushed: Bool {
    guard let vc = navigationController?.viewControllers.first where vc == self else {
        return true
    }

    return false
}
Démosthese
la source
1
que vous devriez toujours faire de toute façon - veuillez expliquer pourquoi?
Alexander Abakumov
Alexander, tu ne devrais pas vraiment.
nickdnk
2

Pour détecter que votre contrôleur est poussé ou non, utilisez simplement le code ci-dessous où vous le souhaitez:

if ([[[self.parentViewController childViewControllers] firstObject] isKindOfClass:[self class]]) {

    // Not pushed
}
else {

    // Pushed
}

J'espère que ce code peut aider n'importe qui ...

Arash Zeinoddini
la source
1
Cette méthode ne fonctionne pas lorsque vous utilisez la même classe de contrôleur de vue à plusieurs endroits, car elle ne vérifie que la classe de celle-ci. Vous pouvez vérifier explicitement l'égalité à la place.
gklka
1

self.navigationController != nil signifierait que c'est dans une pile de navigation.

Daniel
la source
25
Peut toujours être dans un contrôleur de navigation modal
ColdLogic
Ainsi, «modal» et «poussé sur la pile de navigation» ne sont pas mutuellement exclusifs. Penser cela dépend du contexte, mais vérifier si self.navigationController n'est pas nul permet de savoir s'il s'agit d'un contrôleur de vue d'un contrôleur de navigation.
Daniel
@Daniel La différence est entre "poussé" et "présenté". "Modal 'n'a rien à voir avec ça. Je crois que" ColdLogic "voulait dire" présenté "quand ils ont dit" modal ".
rmaddy
1
if let navigationController = self.navigationController, navigationController.isBeingPresented {
    // being presented
}else{
    // being pushed
}
mkto
la source
1

Swift 5
Cette extension pratique gère quelques cas de plus que les réponses précédentes. Ces cas sont VC (contrôleur de vue) est le VC racine de la fenêtre d'application, VC est ajouté en tant qu'enfant au VC parent. Il essaie de renvoyer true uniquement si le viewcontroller est présenté de manière modale.

extension UIViewController {
    /**
      returns true only if the viewcontroller is presented.
    */
    var isModal: Bool {
        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
            return false
        } else if presentingViewController != nil {
            if let parent = parent, !(parent is UINavigationController || parent is UITabBarController) {
                return false
            }
            return true
        } else if let navController = navigationController, navController.presentingViewController?.presentedViewController == navController {
            return true
        } else if tabBarController?.presentingViewController is UITabBarController {
            return true
        }
        return false
    }
}

Merci à la réponse de Jonauz . Là encore, il y a de la place pour plus d'optimisations. Veuillez discuter du cas qui doit être traité dans la section des commentaires.

Mehedi Hasan
la source
0

Si vous utilisez iOS 5.0 ou version ultérieure, veuillez utiliser ce code

-(BOOL)isPresented
{
    if ([self isBeingPresented]) {
        // being presented
         return YES;
    } else if ([self isMovingToParentViewController]) {
        // being pushed
         return NO;
    } else {
        // simply showing again because another VC was dismissed
         return NO;
    }
}
Shahbaz Abbasi
la source
-1

Pour quelqu'un qui se demande, comment dire à ViewController qu'il est présenté

si Aprésente / pousseB

  1. Définir un enumet propertydansB

    enum ViewPresentationStyle {
        case Push
        case Present
    }
    
    //and write property 
    
    var vcPresentationStyle : ViewPresentationStyle = .Push //default value, considering that B is pushed 
  2. Maintenant dans le Acontrôleur de vue, dites Bs'il est présenté / poussé en affectantpresentationStyle

    func presentBViewController() {
        let bViewController = B()
        bViewController.vcPresentationStyle = .Present //telling B that it is being presented
        self.presentViewController(bViewController, animated: true, completion: nil)
    }
  3. Utilisation dans BView Controller

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if self.vcPresentationStyle == .Present {
            //is being presented 
        }
        else {
            //is being pushed
        }
    
    }
Saif
la source
-2
id presentedController = self.navigationController.modalViewController;
if (presentedController) {
     // Some view is Presented
} else {
     // Some view is Pushed
}

Cela vous permettra de savoir si viewController est présenté ou poussé

iCoder86
la source
4
Cette propriété est obsolète.
Morkrom