web-dev-qa-db-fra.com

UIViewController viewWillAppear non appelé lors de l'ajout en tant que sous-vue

J'ai une UIViewController que je charge depuis un autre contrôleur de vue, puis en ajoutant sa vue à une UIScrollView.

self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:@"StatisticsViewController"];
self.statisticsController.match = self.match;

[self.scrollView addSubview:self.statisticsController.view];

J'ai mis des points d'arrêt dans le contrôleur d'affichage des statistiques et viewDidLoad est appelé, mais viewWillAppear ne l'est pas.

Est-ce parce que je ne le pousse pas sur la hiérarchie ou quelque chose?

20
Fogmeister

Vous devez ajouter statisticsController en tant que contrôleur de vue enfant du contrôleur auquel vous ajoutez la vue.

self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:@"StatisticsViewController"];
self.statisticsController.match = self.match;

[self.scrollView addSubview:self.statisticsController.view];
[self addChildViewController:self.statisticsController];
[self.statisticsController didMoveToParentViewController:self];

Je ne suis pas sûr que cela va faire appeler viewDidAppear, mais vous pouvez remplacer didMoveToParentViewController: dans le contrôleur enfant, ce qui sera appelé, de sorte que vous pouvez insérer le code que vous auriez mis dans viewDidAppear.

43
rdelmar

Je rencontre à nouveau le problème -viewWillAppear: non appelé. Après avoir googlé, je suis venu ici. J'ai fait quelques tests et découvert que l'ordre d'appel de -addSubview et -addChildViewController: est important.

Cas 1. déclenchera -viewWillAppear: du contrôleur, mais Cas 2, it WON'T call -viewWillAppear:.

Cas 1:

  controller?.willMoveToParentViewController(self)

  // Call addSubview first
  self.scrollView.addSubview(controller!.view)
  self.addChildViewController(controller!)

  controller!.didMoveToParentViewController(self)

Cas 2:

  controller?.willMoveToParentViewController(self)

  // Call adChildViewController first      
  self.addChildViewController(controller!)      
  self.scrollView.addSubview(controller!.view)

  controller!.didMoveToParentViewController(self)
36
AechoLiu

Par défaut, les rappels d'apparence sont automatiquement transférés aux enfants . Ils sont déterminés à l'aide de la propriété shouldAutomaticallyForwardAppearanceMethods. Vérifiez la valeur de cette propriété, s'il s'agit de NO et si votre viewController enfant doit apparaître directement sur l'apparence du conteneur, vous devez avertir l'enfant avec les méthodes suivantes dans l'implémentation du cycle de vie du contrôleur du conteneur:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    for (UIViewController *child in self.childViewControllers) {
        [child beginAppearanceTransition:YES animated:animated];
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.child endAppearanceTransition];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    for (UIViewController *child in self.childViewControllers) {
        [child beginAppearanceTransition:NO animated:animated];
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.child endAppearanceTransition];
}

Personnalisation du comportement des rappels d'apparence et de rotation

Correction de mon problème! J'espère que ce serait utile. 

16
Alexander Larionov

Comme indiqué dans une autre réponse, le contrôleur de vue parent peut ne pas appeler viewWillAppear etc. lorsque shouldAutomaticallyForwardAppearanceMethods est défini sur false. UINavigationController et UITabBarController sont connus pour le faire. Dans ce cas, vous devez appeler beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) sur le contrôleur de vue enfant avec isAppearing défini sur true lorsque la vue apparaît et inversement.

Vous devez placer ces appels aux endroits appropriés dans votre code, normalement lorsque vous ajoutez et supprimez votre contrôleur de vue enfant.

N'oubliez pas d'appeler endAppearanceTransition sur votre contrôleur de vue enfant lorsque votre transition personnalisée est terminée. Sinon, viewDidAppear et viewDidDisappear ne sont pas appelés.

4
bompf

Selon Apple ( https://developer.Apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html ), l'ordre correct des appels d'API pour ajouter un contrôleur de vue enfant est le suivant:

[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[childVC didMoveToParentViewController:self];

Mais j'avais toujours le problème où viewWillAppear chez l'enfant VC n'était pas appelé sporadiquement. Mon problème était qu'il y avait une condition de concurrence critique qui pouvait entraîner l'exécution du code ci-dessus avant que viewDidAppear dans le contrôleur de vue du conteneur ne soit appelé. S'assurer que viewDidAppear avait déjà été appelé (ou différer l'ajout de l'enfant VC jusqu'à ce qu'il le soit) l'a résolu pour moi.

2
gumbypp

Les réponses précédentes sont correctes, mais si cela peut aider quelqu'un - si vous écrasez loadView dans le contrôleur de vue enfant, aucune autre méthode UIViewController n'est appelée.

Il m’a fallu un peu de temps pour comprendre pourquoi mon code ne fonctionnait pas correctement, jusqu’à ce que j’ai réalisé que j’avais accidentellement remplacé loadView au lieu de viewDidLoad.

1
Liron

Dans mon cas, j'ai trouvé que réécrivait à tort une méthode viewWillAppear: dans ma classe UINavigationController de base:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // other stuff
}

Notez qu'au lieu d'appeler super.viewWillAppear(animated), j'ai écrit super.viewWillDisappear(animated).

Cela a causé que tous les UIViewControllers qui étaient dans cette UINavigationController n'appelaient pas la méthode viewWillAppear:.

Espérons que cela aide quelqu'un sur un problème similaire.

0
Shyngys Kassymov