[ios] Fuite de vues lors de la modification de rootViewController à l'intérieur de transitionWithView



1 Answers

J'ai fait face à ce problème et cela m'a ennuyé pendant toute une journée. J'ai essayé la solution obj-c de @ Rich et il s'avère que quand je veux présenter un autre viewController après cela, je serai bloqué avec un UITransitionView vide.

Finalement, j'ai compris de cette façon et cela a fonctionné pour moi.

- (void)setRootViewController:(UIViewController *)rootViewController {
    // dismiss presented view controllers before switch rootViewController to avoid messed up view hierarchy, or even crash
    UIViewController *presentedViewController = [self findPresentedViewControllerStartingFrom:self.window.rootViewController];
    [self dismissPresentedViewController:presentedViewController completionBlock:^{
        [self.window setRootViewController:rootViewController];
    }];
}

- (void)dismissPresentedViewController:(UIViewController *)vc completionBlock:(void(^)())completionBlock {
    // if vc is presented by other view controller, dismiss it.
    if ([vc presentingViewController]) {
        __block UIViewController* nextVC = vc.presentingViewController;
        [vc dismissViewControllerAnimated:NO completion:^ {
            // if the view controller which is presenting vc is also presented by other view controller, dismiss it
            if ([nextVC presentingViewController]) {
                [self dismissPresentedViewController:nextVC completionBlock:completionBlock];
            } else {
                if (completionBlock != nil) {
                    completionBlock();
                }
            }
        }];
    } else {
        if (completionBlock != nil) {
            completionBlock();
        }
    }
}

+ (UIViewController *)findPresentedViewControllerStartingFrom:(UIViewController *)start {
    if ([start isKindOfClass:[UINavigationController class]]) {
        return [self findPresentedViewControllerStartingFrom:[(UINavigationController *)start topViewController]];
    }

    if ([start isKindOfClass:[UITabBarController class]]) {
        return [self findPresentedViewControllerStartingFrom:[(UITabBarController *)start selectedViewController]];
    }

    if (start.presentedViewController == nil || start.presentedViewController.isBeingDismissed) {
        return start;
    }

    return [self findPresentedViewControllerStartingFrom:start.presentedViewController];
}

Très bien, maintenant tout ce que vous avez à faire est d'appeler [self setRootViewController:newViewController]; lorsque vous voulez changer le contrôleur de vue racine.

Question

En examinant une fuite de mémoire, j'ai découvert un problème lié à la technique d'appel de setRootViewController: intérieur d'un bloc d'animation de transition:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{ self.window.rootViewController = newController; }
                completion:nil];

Si l'ancien contrôleur de vue (celui qui est remplacé) présente actuellement un autre contrôleur de vue, le code ci-dessus ne supprime pas la vue présentée de la hiérarchie de vues.

Autrement dit, cette séquence d'opérations ...

  1. X devient le contrôleur de vue racine
  2. X présente Y, de sorte que la vue de Y est à l'écran
  3. Utilisation de transitionWithView: pour faire de Z le nouveau contrôleur de vue racine

... semble OK pour l'utilisateur, mais l'outil Debug View Hierarchy révèlera que la vue de Y est toujours derrière la vue de Z, à l'intérieur d'un UITransitionView . C'est-à-dire, après les trois étapes ci-dessus, la hiérarchie de vue est:

  • UIWindow
    • UITransitionView
      • UIView (vue de Y)
    • UIView (vue de Z)

Je pense que c'est un problème parce que, au moment de la transition, la vue de X ne fait pas partie de la hiérarchie des vues.

Si j'envoie dismissViewControllerAnimated:NO à X immédiatement avant transitionWithView: dismissViewControllerAnimated:NO la hiérarchie d'affichage résultante est:

  • UIWindow
    • UIView (vue de X)
    • UIView (vue de Z)

Si j'envoie dismissViewControllerAnimated: (YES ou NO) à X, alors effectuez la transition dans le bloc completion: alors la hiérarchie de vue est correcte. Malheureusement, cela interfère avec l'animation. Si animant le renvoi, il perd du temps; Si ce n'est pas animé, ça a l'air cassé.

J'essaie d'autres approches (par exemple, faire une nouvelle classe de contrôleur de vue de conteneur pour servir de contrôleur de vue racine) mais je n'ai rien trouvé qui fonctionne. Je vais mettre à jour cette question au fur et à mesure.

Le but ultime est de passer directement de la vue présentée à un nouveau contrôleur de vue racine, sans laisser de hiérarchies de vues parasites.




Je suis arrivé à ce problème en utilisant ce code:

if var tc = self.transitionCoordinator() {

    var animation = tc.animateAlongsideTransitionInView((self.navigationController as VDLNavigationController).filtersVCContainerView, animation: { (context:UIViewControllerTransitionCoordinatorContext!) -> Void in
        var toVC = tc.viewControllerForKey(UITransitionContextToViewControllerKey) as BaseViewController
        (self.navigationController as VDLNavigationController).setFilterBarHiddenWithInteractivity(!toVC.filterable(), animated: true, interactive: true)
    }, completion: { (context:UIViewControllerTransitionCoordinatorContext!) -> Void in

    })
}

Désactiver ce code, a résolu le problème. J'ai réussi à faire fonctionner ceci en activant seulement cette animation de transition quand la barre de filtre qui s'anime est initialisée.

Ce n'est pas vraiment la réponse que vous cherchez, mais cela pourrait vous apporter le bon tampon pour trouver votre solution.




Related