Réutiliser UIViewController pour des situations modales et non modales
-
22-07-2019 - |
Question
J'ai un contrôleur UIViewController & # 8212; appelons-le "FormController". & # 8212; qui est simplement une forme qui édite un objet. Je veux l'utiliser dans 2 situations différentes:
-
Création d'un nouvel objet & # 8212; & nbsp; à l'aide de la méthode
presentModalViewController:
de UINavigationController. -
Modification d'un objet existant & # 8212; & nbsp; placez le contrôleur de vue sur la pile UINavigationController, sans utiliser de méthode de dialogue.
Il existe une légère différence en ce que, dans la situation modale, j'aimerais une barre d’outils avec " Annuler " et " Terminé " boutons, alors que dans la situation de pile je voudrais juste avoir la barre de navigation fournie par UINavigationController.
Cela serait similaire à l'application Contacts où l'option "Nouveau contact" et le " Modifier contact " les écrans semblent utiliser le même contrôleur de vue, mais le formulaire Nouveau contact est présenté sous forme modale, tandis que l'écran Modifier est placé dans la pile de navigation.
Ma question est la suivante: quel est le meilleur moyen de gérer ces deux situations sans avoir à écrire deux contrôleurs de vue distincts mais essentiellement identiques?
J'ai pensé créer un " ModalFormController " qui encapsule le "FormController" dépouillé; via la composition et ajoute une barre d’outils, mais j’ai lu quelque part dans la documentation que Apple ne recommande pas l’imbrication des contrôleurs de vue.
La solution
Ce que je fais (parfois) consiste à configurer un enum
qui spécifie le type du contrôleur de vue.
Par exemple, vous pouvez avoir deux types: un type Modifier
et un type Ajouter
("nouveau").
Le type Add
est implémenté via un contrôleur de vue modale, tandis que le type Modifier
est placé dans une pile de navigation existante.
Dans la méthode -viewDidLoad:
du contrôleur de vue, je crée simplement une arborescence switch / case
qui définit le titre et d'autres caractéristiques d'apparence en fonction de l'énumération de type spécifiée ci-dessus. .
La bonne chose à ce sujet est qu’il est facile d’ajouter un nouveau type. L'inconvénient est que l'arborescence conditionnelle pour la gestion de cette énumération peut se compliquer rapidement, en fonction de la diversité des types.
Mais l'arborescence switch / case
en facilite la gestion.
Cela dépend donc de ce que vous essayez de faire avec les deux types. Mais c’est tout à fait faisable.
Autres conseils
Pourquoi ne pas utiliser le sous-classement? Faites de ModalCreateFormController
une sous-classe de EditFormController
et gérez les éléments propres à un modal dans la sous-classe.
En plus d'avoir une propriété explicite sur le contrôleur de vue (comme le suggère Alex Reynolds), deux autres approches qui me viennent à l'esprit sont les suivantes:
-
Si vous modifiez un type d'objet de modèle, demandez-lui son état actuel. Si cela a déjà été enregistré, alors vous êtes en mode édition. Sinon, vous êtes en mode création.
-
Regardez la valeur de la propriété
parentViewController
du contrôleur. S'il s'agit d'une instance deUINavigationController
, vous vous trouvez dans la pile de navigation. Si vous êtes affiché en mode modal, ce sera une instance de votre contrôleur de liste.
Ug, je déteste les ivars supplémentaires & # 8230;
J'utilise ceci à la place:
if([[self.navigationController viewControllers] objectAtIndex:0] == self){
//Modal
}else{
//Pushed
}
C'est un peu un bidouillage, mais nous utilisons la logique selon laquelle si le contrôleur de vue incriminé est le premier de la pile, vous ne pouvez pas revenir en arrière. En fait, nous ignorons si cela est affiché modalement.
Je devais le faire plusieurs fois dans mon application et après avoir essayé différentes façons de le faire, y compris des sous-classes modales & amp; classes d'assistants modaux réutilisables qui utilisaient forwardInvocation. J'ai trouvé que le meilleur modèle consistait à créer une méthode contenantModalViewController que chaque contrôleur de vue crée (généralement) et renvoie un UINavigationController que l'appelant utilisera avec presentModalViewController.
Dans la plupart des cas, cette méthode génère et retourne un UINavigationController avec self en tant que contrôleur de vue racine (avec des appels répétés à la méthode vérifiant self.navigationController et le renvoie si ce n'est pas nul). Dans d’autres cas, j’ai créé un contrôleur racine factice d’abord, puis une seconde fois afin d’obtenir un bouton Précédent. Ensuite, une astuce peut être utilisée pour intercepter le bouton Précédent: http: // smallduck .wordpress.com / 2010/10/05 / intercepting-uinavigationcontroller /
Dans certains cas, la vue n’a pas besoin de barre de navigation. Cette méthode ajuste donc simplement certains indicateurs et revient à elle-même. J'ai même constaté que, dans certains cas, qui nécessitaient une barre de navigation, il était plus simple d'invoquer self.view avec cette méthode, puis d'ajuster la hiérarchie des vues pour ajouter un UINavigationBar, puis de revenir à self. Mais dans tous les cas, la configuration est souvent isolée de cette méthode et l'appelant la gère de la même manière.
Apple explique le fonctionnement de l'application de contacts sous le capot:
Pour permettre à une classe de contrôleur de vue personnalisée d'être utilisée à la fois pour afficher et modifier du contenu, substituez la méthode
setEditing: animated:
.
Certaines fonctionnalités sont gratuites, par exemple. bouton Modifier / Terminé
.