52 votes

iOS comment détecter programmatiquement quand le contrôleur de la vue supérieure est ouvert ?

Supposons que j'ai une pile de contrôleurs de navigation avec 2 contrôleurs de vue : VC2 est au-dessus et VC1 est en dessous. Existe-t-il un code que je peux inclure dans VC1 pour détecter que VC2 vient d'être retiré de la pile ?

Puisque j'essaie de détecter le déclenchement de VC2 à partir du code de VC1, il semble que quelque chose comme viewWillAppear o viewDidAppear ne fonctionneront pas, car ces méthodes sont activées chaque fois que VC1 est affiché, y compris lorsqu'il est poussé sur la pile pour la première fois.

EDITAR: il semble que je n'ai pas été très clair avec ma question initiale. Voici ce que j'essaie de faire : déterminer quand le VC1 est affiché parce que le VC2 a été retiré du haut de la pile. Voici ce que je n'essaie PAS de faire : déterminer quand le VC1 est affiché parce qu'il a été poussé sur le haut de la pile. Je dois trouver un moyen de détecter la première action mais PAS la seconde.

Note : Je ne me soucie pas particulièrement du VC2, il peut s'agir de n'importe quel autre VC qui est sorti de la pile, ce qui m'intéresse, c'est le moment où le VC1 redevient le sommet de la pile à cause d'un autre VC qui a commencé à sortir de la pile.

63voto

Ryder Mackay Points 1767

IOS 5 a introduit deux nouvelles méthodes pour gérer exactement ce type de situation. Ce que vous recherchez est -[UIViewController isMovingToParentViewController] . De la docs :

isMovingToParentViewController

Renvoie une valeur booléenne qui indique que le contrôleur de vue est en train d'être ajouté à un parent.

- (BOOL)isMovingToParentViewController

Valeur de retour
YES si le contrôleur de vue apparaît parce qu'il a été ajouté en tant qu'enfant d'un contrôleur de vue du conteneur. sinon, NON.

Discussion
Cette méthode renvoie un OUI uniquement lorsqu'elle est appelée à l'intérieur des méthodes suivantes :

-viewWillAppear:
-viewDidAppear:

Dans votre cas, vous pourriez mettre en œuvre -viewWillAppear: comme ça :

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

    if (self.isMovingToParentViewController == NO)
    {
        // we're already on the navigation stack
        // another controller must have been popped off
    }
}

EDITAR: Il y a une différence sémantique subtile à prendre en compte ici - êtes-vous intéressé par le fait que VC2 en particulier a été retiré de la pile, ou voulez-vous être notifié chaque fois que VC1 est révélé à la suite de tout l'éclatement du contrôleur ? Dans le premier cas, la délégation est une meilleure solution. Une simple référence faible à VC1 peut également fonctionner si vous n'avez pas l'intention de réutiliser VC2.

EDIT 2 : J'ai rendu l'exemple plus explicite en inversant la logique et en ne retournant pas prématurément.

0 votes

Il semble que isMovingToParentViewController renvoie YES lorsqu'un VC est ajouté comme enfant d'un autre VC. Je ne vois pas en quoi cela m'aide à savoir si le VC2 est en train de s'ouvrir. D'ailleurs, je ne me soucie pas de VC2 en particulier, je veux être notifié chaque fois que VC1 est révélé comme le nouveau sommet de la pile.

0 votes

Si le VC1 est révélé suite à l'apparition d'un autre contrôleur dans la pile, -isMovingToParentViewController retournera NO lorsqu'il est appelé depuis -viewWillAppear: . Vous comprenez ?

0 votes

J'appelle cette fonction depuis viewWillAppear de VC1. Et elle renvoie toujours NO, même si le VC2 situé au-dessus du VC1 a été ouvert lorsque le VC1 est apparu. Ma solution utilise simplement une propriété hasAppeared. Le problème est que le contrôleur de vue parent de VC1 n'est pas navigationController, mais tabBarController.

15voto

snarshad Points 149

IsMovingTo/FromParentViewController ne fonctionnera pas pour pousser et sauter dans une pile de contrôleurs de navigation.

Voici un moyen fiable de le faire (sans utiliser le délégué), mais c'est probablement réservé à iOS 7+.

UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey];

if ([[self.navigationController viewControllers] containsObject:fromViewController])
{
    //we're being pushed onto the nav controller stack.  Make sure to fetch data.
} else {
    //Something is being popped and we are being revealed
}

Dans mon cas, l'utilisation du délégué signifierait que le comportement des contrôleurs de vue serait plus étroitement couplé avec le délégué qui possède la pile de navigation, et je voulais une solution plus autonome. Cela fonctionne.

0 votes

Vérification de isMovingFromParentViewController() sur viewWillDisappear(_:) fonctionne bien pour moi lorsqu'il s'agit de déterminer si un contrôleur de vue est en train de s'ouvrir.

0 votes

C'est la meilleure réponse/solution à cette question en ce qui me concerne, car elle s'accroche directement au contrôleur de navigation, et vous dira si vous êtes le contrôleur de vue qui est retiré de la pile, ou si vous êtes le contrôleur de vue qui est élevé à la suite d'un ou plusieurs éléments retirés. Merci !

7voto

Brad Eaton Points 86

Une façon d'aborder ce problème serait de déclarer un protocole de délégué pour VC2, comme ceci :

dans VC1.h

@interface VC1 : UIViewController <VC2Delegate> {
...
}

dans VC1.m

-(void)showVC2 {
    VC2 *vc2 = [[VC2 alloc] init];
    vc2.delegate = self;
    [self.navigationController pushViewController:vc2 animated:YES];
}

-(void)VC2DidPop {
    // Do whatever in response to VC2 being popped off the nav controller
}

dans VC2.h

@protocol VC2Delegate <NSObject>
-(void)VC2DidPop;
@end

@interface VC2 : UIViewController {

    id<VC2Delegate> delegate;
}

@property (nonatomic, assign) id delegate;

...

@end

VC2.m

-(void)viewDidUnload {
    [super viewDidUnload];
    [self.delegate VC2DidPop];
}

Il existe un bon article sur les bases des protocoles et des délégués. aquí .

4 votes

- (void)viewDidUnload est obsolète depuis iOS 6.0.

0voto

darvids0n Points 7914

Qu'est-ce que vous essayez de faire spécifiquement ?

Si vous essayez de détecter que le VC1 est sur le point d'être montré, este La réponse devrait vous aider. Utilisez UINavigationControllerDelegate .

Si vous essayez de détecter que VC2 est sur le point d'être caché, j'utiliserais simplement la fonction viewWillDisappear: de VC2.

0 votes

J'ai besoin de détecter, à partir de VC1, quand le contrôleur de vue au sommet de VC1 (qui dans mon exemple est VC2) a été retiré, révélant VC1 comme le nouveau sommet de la pile. J'ai consulté la réponse dont vous avez donné le lien, mais il semble que la méthode déléguée willShowViewController ne puisse pas faire la distinction entre VC1 qui est affiché parce qu'il est poussé sur la pile, et VC1 qui est affiché parce que VC2 est sorti de la pile.

0 votes

Une solution simple à ce problème est d'initialiser VC1 avec un fichier BOOL réglé sur NO alors dans viewWillAppear: vous vérifiez le BOOL . Si le BOOL es NO et le régler sur YES ou de procéder autrement à votre traitement. Cela garantit que lorsque VC1 est poussé sur la pile pour la première fois, le traitement n'est pas effectué, mais dans tous les autres cas, il l'est.

0voto

Zhang Points 3585

Vous pouvez ajouter un observateur pour NSNotification spécifiquement JUST pour votre VC2.

// pasing the "VC2" here will tell the notification to only listen for notification from
// VC2 rather than every single other objects
[[NSNotitificationCenter defaultCenter] addObserver:self selector:@selector(doSomething:) object:VC2];

Maintenant dans la vue de votre VC2 va disparaître, vous pouvez poster une notification :

-(void)viewWillDisappear
{
    [[NSNotificationCenter defaultCenter] postNotificationNamed:@"notif_dismissingVC2" object:nil];
}

0 votes

Ajouter if (self.isMovingFromParentViewController) et c'est la meilleure réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X