web-dev-qa-db-fra.com

viewWillDisappear: Détermine si le contrôleur de vue est affiché ou affiche un contrôleur de sous-vue

J'ai du mal à trouver une bonne solution à ce problème. Dans le contrôleur de vue -viewWillDisappear: _ méthode, je dois trouver un moyen de déterminer si c'est parce qu'un contrôleur de vue est poussé sur la pile du contrôleur de navigation, ou si c'est parce que le contrôleur de vue est en train de disparaître car il a été sauté.

Pour le moment, je mets des drapeaux tels que isShowingChildViewController mais cela devient assez compliqué. Le seul moyen que je pense pouvoir détecter est le -dealloc méthode.

132
Michael Waterfall

Vous pouvez utiliser ce qui suit.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

Ceci est bien sûr possible parce que la pile de contrôleur de vue UINavigationController (exposée via la propriété viewControllers) a été mise à jour au moment de l'appel de viewWillDisappear.

228
Bryan Henry

Je pense que le moyen le plus simple est:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

Rapide:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParentViewController
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}
128
hacker2007

Extrait de la documentation Apple dans UIViewController.h:

"Ces quatre méthodes peuvent être utilisées dans les rappels d’apparence d’un contrôleur de vue pour déterminer si celui-ci est présenté, rejeté ou ajouté ou supprimé en tant que contrôleur de vue enfant. Par exemple, un contrôleur de vue peut vérifier s’il est en train de disparaître sauté en se posant la question dans sa méthode viewWillDisappear: en vérifiant l'expression ([self isBeingDismissed] || [self isMovingFromParentViewController]). "

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

Alors oui, le seul moyen documenté de faire cela est le suivant:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Swift 3 version:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}
58
Shyam Bhat

Si vous voulez juste savoir si votre vue est en train de s’écrouler, je viens de découvrir que self.navigationController est nil dans viewDidDisappear, lorsqu'il est retiré de la pile de contrôleurs. Donc, c'est un test alternatif simple.

(Ceci, je le découvre après avoir essayé toutes sortes de contorsions. Je suis surpris qu'il n'y ait pas de protocole de contrôleur de navigation pour enregistrer un contrôleur de vue à notifier sur des pops. Vous ne pouvez pas utiliser UINavigationControllerDelegate car cela fonctionne réellement. .)

18
dk.

Swift 4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }
11
Umair

En rapide:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}
6
user754905

Je trouve la documentation d'Apple à ce sujet difficile à comprendre. Cette extension permet de voir les états à chaque navigation.

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}
2
Norman

Cette question est assez ancienne mais je l'ai vue par accident et je souhaite donc publier les meilleures pratiques (autant que je sache).

tu peux juste faire

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}
1
user1396236

Ceci s’applique à iOS7, aucune idée s’il s’applique à d’autres. D'après ce que je sais, dans viewDidDisappear, la vue a déjà été affichée. Ce qui signifie que vous interrogez self.navigationController.viewControllers vous obtiendrez un nil. Alors, vérifiez si c'est nul.

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }
1
Byte

Les séquences peuvent être un moyen très efficace de gérer ce problème dans iOS 6+. Si vous avez attribué un identifiant à la séquence en question dans Interface Builder, vous pouvez le rechercher dans prepareForSegue.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a Push has occurred that will be checked later
    }
}
1
Kyle Clegg

Merci @Bryan Henry, travaille toujours dans Swift 5

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }
0
dengST30