Je ne fais que plonger pour la première fois dans le développement iOS, et l'une des premières choses à faire est d'implémenter un contrôleur de vue de conteneur personnalisé - appelons-le SideBarViewController
- qui écarte celui de plusieurs contrôleurs de vue enfant possibles qu'il affiche, presque exactement comme un contrôleur de barre d'onglets standard . (C'est à peu près un contrôleur de barre d'onglets mais avec un menu latéral masquable au lieu d'une barre d'onglets.)
Selon les instructions de la documentation Apple, j'appelle addChildViewController
chaque fois que j'ajoute un ViewController enfant à mon conteneur. Mon code pour permuter le contrôleur de vue enfant actuel affiché par le SideBarViewController
ressemble à ceci:
- (void)showViewController:(UIViewController *)newViewController {
UIViewController* oldViewController = [self.childViewControllers
objectAtIndex:0];
[oldViewController removeFromParentViewController];
[oldViewController.view removeFromSuperview];
newViewController.view.frame = CGRectMake(
0, 0, self.view.frame.size.width, self.view.frame.size.height
);
[self addChildViewController: newViewController];
[self.view addSubview: newViewController.view];
}
Ensuite, j'ai commencé à essayer de comprendre ce que addChildViewController
fait ici, et je me suis rendu compte que je n'en ai aucune idée. En plus de coller le nouveau ViewController
dans le .childViewControllers
tableau, il semble n’avoir d’effet sur rien. Les actions et les sorties de la vue du contrôleur enfant au contrôleur enfant que j'ai défini sur le storyboard fonctionnent toujours bien même si je n'appelle jamais addChildViewController
, et je ne peux pas imaginer ce que cela pourrait affecter.
En effet, si je réécris mon code pour ne pas appeler addChildViewController
, mais plutôt comme ceci ...
- (void)showViewController:(UIViewController *)newViewController {
// Get the current child from a member variable of `SideBarViewController`
UIViewController* oldViewController = currentChildViewController;
[oldViewController.view removeFromSuperview];
newViewController.view.frame = CGRectMake(
0, 0, self.view.frame.size.width, self.view.frame.size.height
);
[self.view addSubview: newViewController.view];
currentChildViewController = newViewController;
}
... alors mon application fonctionne toujours parfaitement, autant que je sache!
La documentation Apple n'apporte pas beaucoup de lumière sur ce que addChildViewController
fait, ni pourquoi nous sommes supposés l'appeler. Toute l'étendue de la description pertinente de ce que fait la méthode ou pourquoi il devrait être utilisé dans sa section dans le UIViewController
Référence de la classe est, à l'heure actuelle:
Ajoute le contrôleur de vue donné en tant qu'enfant. ... Cette méthode est uniquement destinée à être appelée par une implémentation d'un contrôleur de vue de conteneur personnalisé. Si vous substituez cette méthode, vous devez appeler super dans votre implémentation.
Il y a aussi ce paragraphe plus tôt sur la même page:
Votre contrôleur de vue conteneur doit associer un contrôleur de vue enfant à lui-même avant d'ajouter la vue racine de l'enfant à la hiérarchie de vues. Cela permet à iOS de router correctement les événements vers les contrôleurs de vue enfant et les vues que ces contrôleurs gèrent. De même, après avoir supprimé la vue racine d’un enfant de sa hiérarchie, il doit déconnecter ce contrôleur de vue enfant de lui-même. Pour faire ou rompre ces associations, votre conteneur appelle des méthodes spécifiques définies par la classe de base. Ces méthodes ne sont pas destinées à être appelées par les clients de votre classe de conteneur; ils ne doivent être utilisés que par l’implémentation de votre conteneur pour fournir le comportement de confinement attendu.
Voici les méthodes essentielles que vous pourriez avoir besoin d'appeler:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
mais cela ne donne aucune indication sur ce que sont les "événements" ou le "comportement de confinement attendu" dont il parle, ni pourquoi pourquoi (ou même quand) appeler ces méthodes est "essentiel".
Les exemples de contrôleurs de vue de conteneur personnalisés de la section "Contrôleurs de vue de conteneur personnalisés" de la documentation Apple appellent tous cette méthode). un tableau, mais je ne peux pas comprendre quel est cet objectif. Que fait cette méthode et pourquoi devrais-je l'appeler?
Je m'interrogeais aussi sur cette question. J'ai regardé Session 102 de la WWDC 2011 vidéos et M. View Controller, Bruce D. Nilo , a déclaré ceci:
viewWillAppear:
,viewDidAppear:
, etc n'a rien à voir avecaddChildViewController:
. Tout çaaddChildViewController:
fait est de dire "Ce contrôleur de vue est un enfant de celui-ci" et cela n’a rien à voir avec l’apparence de la vue. Quand ils sont appelés, ils sont associés au moment où les vues entrent et sortent de la hiérarchie des fenêtres.
Il semble donc que l'appel à addChildViewController:
fait très peu. Les effets secondaires de l'appel sont la partie importante. Ils proviennent des relations parentViewController
et childViewControllers
. Voici certains des effets secondaires que je connais:
transitionFromViewController:toViewController:…
où les deux VC doivent avoir le même parentnavigationController
, tabBarController
, etc.Je pense qu'un exemple vaut mille mots.
Je travaillais sur une application de bibliothèque et je voulais afficher une vue du bloc-notes de Nice qui apparaît lorsque l'utilisateur souhaite ajouter une note.
Après avoir essayé quelques solutions, j'ai fini par inventer ma propre solution personnalisée pour afficher le bloc-notes. Ainsi, lorsque je souhaite afficher le bloc-notes, je crée une nouvelle instance de NotepadViewController
et ajoute sa vue racine en tant que sous-vue à la vue principale. Jusqu'ici tout va bien.
Ensuite, j'ai remarqué que l'image du bloc-notes est partiellement masquée sous le clavier en mode paysage.
Je voulais donc changer l'image du bloc-notes et la déplacer vers le haut. Et pour ce faire, j’ai écrit le code approprié dans willAnimateRotationToInterfaceOrientation:duration:
_ méthode, mais quand j'ai lancé l'application, rien ne s'est passé! Et après le débogage, j'ai remarqué qu'aucune des méthodes de rotation de UIViewController
n'est en réalité appelée dans NotepadViewController
. Seules les méthodes du contrôleur de vue principale sont appelées.
Pour résoudre ce problème, je devais appeler toutes les méthodes de NotepadViewController
manuellement quand elles sont appelées dans le contrôleur de vue principal. Cela compliquera bientôt les choses et créera une dépendance supplémentaire entre des composants non liés de l'application.
C'était du passé, avant que le concept de contrôleur de vue enfant ne soit introduit. Mais maintenant, il vous suffit de addChildViewController
sur le contrôleur de la vue principale et tout fonctionnera comme prévu sans autre travail manuel.
Edit: Il existe deux catégories d'événements qui sont transférés aux contrôleurs de vue enfant:
1- Méthodes d'apparence:
- viewWillAppear:
- viewDidAppear:
- viewWillDisappear:
- viewDidDisappear:
2- Méthodes de rotation:
- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:
Vous pouvez également contrôler les catégories d'événements que vous souhaitez transférer automatiquement en remplaçant shouldAutomaticallyForwardRotationMethods
et shouldAutomaticallyForwardAppearanceMethods
.
-[UIViewController addChildViewController:]
ajoute uniquement le contrôleur de vue passé dans un tableau de viewControllers qu'un viewController (le parent) veut conserver en référence. Vous devriez en fait ajouter vous-même les vues de viewController à l'écran en les ajoutant en tant que sous-vues d'une autre vue (par exemple, la vue de parentViewController). Il existe également un objet de commodité dans Interface Builder qui permet d’utiliser childrenViewControllers dans Storyboards.
Auparavant, pour conserver la référence des autres viewControllers dont vous utilisiez les vues, vous deviez en conserver la référence manuelle dans @properties. Avoir une propriété prédéfinie comme childViewControllers
et par conséquent parentViewController
est un moyen pratique de gérer de telles interactions et de créer des viewControllers composés, comme UISplitViewController, que vous trouvez dans les applications iPad.
De plus, childrenViewControllers reçoit automatiquement tous les événements système reçus par le parent: -viewWillAppear, -viewWillDisappear, etc. Auparavant, vous auriez dû appeler cette méthode manuellement sur vos "childrenViewControllers".
C'est ça.