web-dev-qa-db-fra.com

iOS comment détecter par programme lorsque le contrôleur de vue de dessus est sauté?

Supposons que j'ai une pile de contrôleurs nav avec 2 contrôleurs de vue: VC2 est en haut et VC1 est en dessous. Y a-t-il du code que je peux inclure dans VC1 qui détectera que VC2 vient d'être sorti de la pile?

Puisque j'essaie de détecter l'éclatement de VC2 à partir du code de VC1, il semble que quelque chose comme viewWillAppear ou viewDidAppear ne fonctionnera pas, car ces méthodes se déclenchent à chaque fois VC1 s'affiche, y compris lorsqu'il est poussé pour la première fois sur la pile.

EDIT: il semble que je n'étais pas très clair avec ma question d'origine. Voici ce que j'essaie de faire: déterminer quand VC1 est affiché en raison du fait que VC2 est sorti du haut de la pile. Voici ce que je n'essaye PAS de faire: déterminer quand VC1 est affiché en raison de son enfoncement en haut de la pile. J'ai besoin d'un moyen qui détecte la première action mais PAS la deuxième action.

Remarque: je ne me soucie pas particulièrement de VC2, il peut y avoir un certain nombre d'autres VC qui sortent de la pile, ce qui m'importe, c'est quand VC1 redevient le haut de la pile à cause d'un autre VC commence à sortir du haut.

52
JMLdev

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
OUI si le contrôleur de vue apparaît parce qu'il a été ajouté en tant qu'enfant d'un contrôleur de vue de conteneur, sinon NON.

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

-viewWillAppear:
-viewDidAppear:

Dans votre cas, vous pouvez implémenter -viewWillAppear: Comme ceci:

- (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
    }
}

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

EDIT 2: J'ai rendu l'exemple plus explicite en inversant la logique et en ne revenant pas tôt.

63
Ryder Mackay

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

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

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 soit plus étroitement associé au délégué qui possède la pile de navigation, et je voulais une solution plus autonome. Cela marche.

15
snarshad

Une façon dont vous pourriez aborder cela serait de déclarer un protocole délégué pour VC2 quelque chose 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 y a un bon article sur les bases des protocoles et des délégués ici .

7
Brad Eaton

Vous pouvez également détecter dans le contrôleur de vue qui apparaît

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

    if ([self isMovingFromParentViewController]) {
        ....
    }
}
4
Saren Inden

Cela fonctionne pour moi

UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey];
if (![[self.navigationController viewControllers] containsObject:fromViewController] && !self.presentedViewController)
{
  //Something is being popped and we are being revealed 
}
1
chuanfeng

Swift 3

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if self.isMovingToParentViewController {
            print("View is moving to ParentViewControll")
        }
}
0
iParesh

Oui, dans VC1 vous pouvez vérifier si VC2 est sauté ou non. UINavigationController, il existe une méthode viewControllers qui renvoie le tableau des push Contrôleurs, qui sont dans la pile (c'est-à-dire qui ont été poussés).

Vous parcourez donc la boucle en comparant la classe. Si VC2 est là, aura une correspondance, sinon non.

0
Sandy

Qu'essayez-vous spécifiquement de faire?

Si vous essayez de détecter que VC1 est sur le point d'être affiché, la réponse this devrait vous aider. Utilisez INavigationControllerDelegate .

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

0
darvids0n

J'ai eu la même situation mais avec un cas d'utilisation légèrement plus spécifique. Dans mon cas, nous voulions déterminer si un VC1 est apparu/affiché lorsque l'utilisateur tapait sur le bouton retour de VC2 où VC2 est poussé sur navigationController sur VC1.

J'ai donc utilisé l'aide de réponse de snarshad pour personnaliser selon mes besoins. Voici le code dans viewDidAppear de VC1 dans Swift 3 .

// VC1: ParentViewController
// VC2: ChildViewController

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

        if let transitionCoordinator = navigationController?.transitionCoordinator,
            let fromVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.to),
            fromVC is ChildViewController,
            toVC is ParentViewController {

            print("Back button pressed on ChildViewController, and as a result ParentViewController appeared")
        }
    }
0
BLC