web-dev-qa-db-fra.com

Quel est le processus d'une naissance UIViewController (quelle méthode suit laquelle)?

Il existe de nombreuses méthodes pour remplacer, comme initWithNibname:, awakeFromNib, loadView, viewDidLoad, viewDidAppear:, layoutSubviews, et je ne peux tout simplement pas décider dans quel ordre ces méthodes seront appelées.

Je viens de remplacer l'un d'eux "par cœur".

Une explication détaillée?

57
Geri

Il se passe beaucoup de choses dans les coulisses avec Cocoa view et viewController management .

1. L'objet viewController

À sa base, un viewController est un objet contrôleur générique. Lorsqu'il est alloué pour la première fois à un objet initialisé, aucun objet de vue ne lui est associé. La vue n'est instanciée que lorsque (et si) elle est requise. Ainsi, sans tenir compte de la vue, le cycle de vie d'un viewController est le même que tout autre objet:

UIViewController * myVC = [[UIViewController alloc] initWith...];
...
[myVC release];

L'initialiseur désigné pour viewControllers est -initWithNibname:bundle: . Si vous spécifiez une pointe, le viewController peut charger automatiquement sa vue à partir de cette pointe et connecter les IBOutlets que vous avez définies (voir ci-dessous pour plus de détails).

2. Chargement et déchargement de la vue

Un viewController chargera sa vue selon les besoins. Cela se produit généralement lorsque le -view est appelée pour la première fois et peut se produire à tout moment dans votre programme, selon la façon dont vous initialisez votre interface utilisateur. La vue peut également être détruite et rechargée plusieurs fois pendant la durée de vie de votre programme, selon la façon dont vous gérez votre interface utilisateur. Lorsque le viewController a identifié que sa vue est requise mais pas encore chargée, le -loadView sera appelée. Le flux de messages de base se présente comme suit:

view
  loadView
  viewDidLoad

Notez que si vous remplacez le -view méthode, -loadView et viewDidLoad ne seront pas appelés automatiquement. Si vous remplacez -loadView, you must définissez la propriété view de viewController. Sinon, le prochain appel à -view déclenchera à nouveau le processus de chargement.

La vue peut également être déchargée à tout moment pendant la durée de vie de votre programme simplement en définissant la propriété view sur nil. L'implémentation par défaut de -didReceiveMemoryWarning le fera automatiquement, tant que la vue n'a pas de vue d'ensemble (c'est-à-dire si elle ne fait pas actuellement partie de la hiérarchie de vue active). Le flux de messages se déroule comme suit:

view = nil
   viewDidUnload

2a. Chargement de la vue par programme

Si vous choisissez de remplacer -loadView, vous pouvez créer une vue, des sous-vues, d'autres viewControllers et toutes les connexions entre ces objets comme bon vous semble. Bien sûr, cela signifie que vous êtes également responsable de la gestion de la mémoire en ce qui concerne les objets que vous créez. Si votre sous-classe remplace -loadView, il doit être initialisé en utilisant nil pour nibName et bundle.

2b. Chargement de la vue depuis une plume

Si vous utilisez un fichier nib, l'implémentation par défaut de -loadView ouvrira automatiquement ce fichier nib, instanciera ses objets, ajoutera toutes les connexions entre eux et prendra soin de la gestion de la mémoire pour vous.

Les choses deviennent un peu plus délicates avec les fichiers nib, car il se passe tellement de choses en arrière-plan. Le -awakeFromNib est appelée pour chaque objet qui est instanciée lorsqu'un fichier nib est chargé, et il n'y a aucune garantie que les autres objets du fichier nib auront été entièrement chargés lors de son appel.

3. Affichage des vues

-viewWillAppear:, -viewDidAppear:, -viewWillDisappear: et -viewDidDisappear: ne sont appelés que lorsque la vue est affichée ou masquée à l'écran, en particulier pendant les transistions animées d'une vue à l'autre. Ces méthodes peuvent être appelées plusieurs fois pendant la durée de vie de votre programme, car les vues sont échangées dans et hors de votre schéma de navigation.

4. Afficher la disposition

Le -layoutSubviews la méthode est pas partie de UIViewController. Il est appelé pour les objets UIView lorsque leurs limites ont été modifiées. Si vous utilisez une sous-classe UIView personnalisée dans votre programme, cette méthode peut être utilisée pour créer une présentation de sous-vue personnalisée au lieu de s'appuyer sur les méthodes de redimensionnement automatique par défaut de Cocoa.

5. Tout mettre ensemble

En raison de la complexité, il existe de nombreuses façons différentes pour que ce processus se produise, mais un calendrier normal pourrait ressembler à ceci:

-[viewController initWithNibname:Bundle:]
-[viewController awakeFromNib]
-[viewController loadView]
-[view awakeFromNib]
-[viewController viewDidLoad]
-[viewController viewWillAppear]
-[viewController viewDidAppear]
...
-[viewController viewWillDisappear]  // user navigated away
-[viewController viewDidDisappear]
...
-[viewController viewWillAppear]     // user navigated back
-[viewController viewDidAppear]
...
-[viewController viewWillDisappear]  // user navigated away
-[viewController viewDidDisappear]
...
-[viewController setView:nil]        // memory warning, perhaps
-[viewController viewDidUnload]
...
-[viewController loadView]           // user navigated back
-[view awakeFromNib]
-[viewController viewDidLoad]
-[viewController viewWillAppear]
-[viewController viewDidAppear]
...
173
e.James

J'ai revisité ces derniers temps et créé un projet de test: https://github.com/Janek2004/ViewControllerTest

Exécutez le projet sur le simulateur iOS pour voir l'ordre d'exécution des méthodes de la sous-classe UIViewController. L'ordre peut être différent chaque fois que nous utilisons le fichier Nib au lieu du storyboard ou du contrôleur de vue par programme.

  1. - [ViewController initWithCoder:] Désarchiver les données de la plume ou du storyboard
  2. - [ViewController awakeFromNib] Prépare le récepteur pour le service après qu'il a été chargé à partir d'une archive Interface Builder ou d'un fichier nib.
  3. - [ViewController loadView] Vous ne devez jamais appeler cette méthode directement. Le contrôleur de vue appelle cette méthode lorsque sa propriété view est demandée mais est actuellement nulle. Cette méthode charge ou crée une vue et l'affecte à la propriété view.
  4. - [ViewController viewDidLoad] Cette méthode est appelée après que le contrôleur de vue a chargé sa hiérarchie de vues en mémoire.
  5. - [ViewController viewWillAppear:] Cette méthode est appelée avant que la vue du récepteur soit sur le point d'être ajoutée à une hiérarchie de vues et avant que des animations ne soient configurées pour afficher le vue.
  6. - [ViewController viewWillLayoutSubviews] Appelé pour informer le contrôleur de vue que sa vue est sur le point de disposer ses sous-vues. Lorsque les limites d'une vue changent, la vue ajuste la position de ses sous-vues. Votre contrôleur de vue peut remplacer cette méthode pour apporter des modifications avant que la vue ne présente ses sous-vues.
  7. - [ViewController viewDidLayoutSubviews] Appelé pour informer le contrôleur de vue que sa vue vient de présenter ses sous-vues. Lorsque les limites changent pour la vue d'un contrôleur de vue, la vue ajuste les positions de ses sous-vues, puis le système appelle cette méthode. Toutefois, cette méthode appelée n'indique pas que les dispositions individuelles des sous-vues de la vue ont été ajustées. Chaque sous-vue est chargée d'ajuster sa propre mise en page.
  8. - [ViewController viewDidAppear:] Avertit le contrôleur de vue que sa vue a été ajoutée à une hiérarchie de vues. Vous pouvez remplacer cette méthode pour effectuer des tâches supplémentaires associées à la présentation de la vue.

  9. - [ViewController viewWillDisappear:] Avertit le contrôleur de vue que sa vue est sur le point d'être supprimée d'une hiérarchie de vues. Cette méthode est appelée en réponse à une vue en cours de suppression d'une hiérarchie de vues. Cette méthode est appelée avant la suppression effective de la vue et avant la configuration des animations. Avertit le contrôleur de vue que sa vue a été ajoutée à une hiérarchie de vues. Vous pouvez remplacer cette méthode pour effectuer des tâches supplémentaires associées à la présentation de la vue.

  10. - [ViewController viewDidDisappear:] Avertit le contrôleur de vue que sa vue a été supprimée d'une hiérarchie de vues.
36
Janusz Chudzynski

Un autre moment clé du processus est lorsque layoutSubviews est appelé sur n'importe quelle sous-vue. C'est à ce stade, et pas plus tôt, que les contraintes configurées dans le storyboard ont été appliquées. Si vous devez apporter des ajustements aux sous-vues d'une vue, en fonction de ses coordonnées contraintes, vous devez le faire dans layoutSubviews. Si vous le faites dans viewDidLayoutSubviews, ce sera trop tôt, car ces sous-vues n'ont pas encore appliqué leurs contraintes (car, comme le dit la documentation "Chaque sous-vue est responsable de l'ajustement de sa propre disposition".) Et si vous le faites dans viewDidAppear, ce sera trop tard évidemment, car l'utilisateur verra vos sous-vues changer les coordonnées. Ainsi, l'autre étape vitale du processus est:

-viewController viewWillAppear
-viewController viewWillLayoutSubviews
-viewController viewDidLayoutSubviews
---> viewController.[any subview] layoutSubviews
-viewController viewDidAppear  
9
Harris

de Apple documentation UIViewController:

Lorsque vous définissez une nouvelle sous-classe de UIViewController, vous devez spécifier les vues à gérer par le contrôleur. Il existe deux façons mutuellement exclusives de spécifier ces vues: manuellement ou à l'aide d'un fichier nib. Si vous spécifiez les vues manuellement, vous devez implémenter la méthode loadView et l'utiliser pour affecter un objet de vue racine à la propriété view. Si vous spécifiez des vues à l'aide d'un fichier nib, vous ne devez pas remplacer loadView, mais plutôt créer un fichier nib dans Interface Builder, puis initialiser votre objet contrôleur de vue à l'aide de la méthode initWithNibName: bundle:. La création de vues à l'aide d'un fichier nib est souvent plus simple car vous pouvez utiliser l'application Interface Builder pour créer et configurer vos vues graphiquement (par opposition à un programme). Cependant, les deux techniques ont le même résultat final, qui consiste à créer l'ensemble approprié de vues et à les exposer via la propriété view.

Du haut de ma tête:

  1. initWithNibname
  2. loadView (charger des trucs manuellement)
  3. viewDidiLoad
  4. viewDidAppear

aucun indice où layoutSubviews entre

4
LordT

Je résout généralement cette question en mettant un NSLog (ou des points d'arrêt) dans tous ces délégués, y compris le délégué de lancement de l'application, et en suivant l'ordre dans le débogueur.

1
hotpaw2
 - Ceci est lié à l'affichage uniquement: 
 - viewWillAppear: 
 - viewDidAppear: 
 - viewWillDisappear: et 
 - viewDidDisappear: 
 
 ne sont appelés que lorsque la vue est affichée. 
 
 - viewController viewDidLoad 
 - viewController viewWillAppear 
 - viewController viewDidAppear 
 
 autres méthodes 
 
 - viewController viewDidDisappear 
 - viewController viewWillDisappear 
 - viewController viewDidUnload 
1
Ram S

Je tiens à remercier e.James pour son excellente description. Je ne peux pas encore commenter un message, mais pour une illustration visuelle rapide, reportez-vous à cet organigramme dans le guide de programmation de View Controller. Et je me rends compte que c'est hors sujet, mais pour un graphique de la séquence de lancement de l'application , reportez-vous au Guide de programmation d'application iOS.

0
Chris Conover