2
votes

Modèle iOS Swift Coordinator et bouton de retour du contrôleur de navigation

J'utilise le modèle MVVM + Coordinator . Tous mes contrôleurs sont créés par des coordinateurs . Mais quelle est la bonne façon d'arrêter mes coordinateurs en appuyant sur le bouton de retour du Navigation Controller?

class InStoreMainCoordinator: NavigationCoordinatorType, HasDisposeBag {

    let container: Container

    enum InStoreMainChildCoordinator: String {
        case menu = "Menu"
        case locations = "Locations"
    }

    var navigationController: UINavigationController
    var childCoordinators = [String: CoordinatorType]()

    init(navigationController: UINavigationController, container: Container) {
        self.navigationController = navigationController
        self.container = container
    }

    func start() {
        let inStoreMainViewModel = InStoreMainViewModel()
        let inStoreMainController = InStoreMainController()
        inStoreMainController.viewModel = inStoreMainViewModel

        navigationController.pushViewController(inStoreMainController, animated: true)
    }
}


0 commentaires

4 Réponses :


1
votes

Mon approche consiste à utiliser un coordinateur racine (parent) qui gère les coordinateurs enfants, donc lorsque l'utilisateur termine un flux ou appuie sur le bouton retour, une méthode déléguée dans le coordinateur racine est appelée et il peut nettoyer le coordinateur enfant et créer un nouveau un si nécessaire.


0 commentaires

1
votes

Le motif du coordinateur a un angle mort connu concernant le bouton de retour natif. Vous avez principalement deux façons de résoudre ce problème:

  • Réimplémentez votre propre bouton de retour, même si vous perdez le geste natif de glisser vers l'arrière pour revenir en arrière.
  • Implémentez UINavigationControllerDelegate pour détecter lorsqu'une vue est apparue afin de pouvoir désallouer le coordinateur correspondant.

En ce qui concerne la première solution, je ne suggère pas celle-ci, l'utilisateur paierait le prix de votre architecture de code, cela ne semble pas juste.

Pour le second, vous pouvez l'implémenter sur le coordinateur lui-même comme suggéré par @mosbah, mais je vous suggère d'aller plus loin et de séparer la navigation vers le coordinateur en utilisant un NavigationController ou Classe de routeur pour isoler la navigation elle-même et maintenir une séparation claire des préoccupations.

J'ai écrit quelque chose à ce sujet ici qui détaille les principales étapes.


1 commentaires

Il existe une troisième solution. Utilisez une fonction au lieu d'une classe pour représenter votre coordinateur. Vous n'avez alors aucun problème de propriété à craindre.



1
votes

Ce que je fais maintenant, après avoir lu de nombreux articles sur les coordinateurs et vu des idées complexes telles que les routeurs, des délégués magiques et des contrôleurs de navigation personnalisés, c'est:

View Controller possède fortement Coordinator, et Coordinator a une faible référence à View Controller, voire pas du tout. Le coordinateur a une faible référence à son parent, pour soutenir la chaîne de responsabilité pour la communication entre les objets du coordinateur.

(Exemple de modèle de conception de chaîne de responsabilité serait la chaîne de répondeurs dans iOS.)

Au moment où vous appelez stop sur un coordinateur, le contrôleur de vue sort de la pile, libère et libère le coordinateur. Ainsi, lorsque le bouton de retour est appuyé et que le contrôleur de vue est rejeté, le coordinateur est désalloué.

Cela fonctionne pour moi car il n'est pas nécessaire de créer une infrastructure supplémentaire.

Au départ, j'ai résolu le problème d'UINavigationControllerDelegate en créant la classe NavigationControllerMutliDelegate qui était conforme au protocole UINavigationControllerDelegate. Il avait une logique d'enregistrement / désinscription. Ensuite, cet objet a été transmis à chaque coordinateur pour notifier le coordinateur lorsque le contrôleur de vue se ferme. NavigationControllerMutliDelegate était un exemple de modèle de conception de visiteur, il y avait un groupe de coordinateurs et sur View Controller, il a averti tous les coordinateurs en envoyant un objet à chacun.

Mais, à la fin, quand j'ai vu la quantité de code et la complexité inutile, je suis simplement allé avec View Controller possédant Coordinator. Je veux juste que l'objet soit au-dessus de View Controller qui conserve les fournisseurs de données, les services, les modèles de vue, etc. pour que View Controller soit plus propre. Je ne veux pas réinventer la pile pop de coordinateurs et gérer autant de problèmes de propriétaire. Comme si je voulais quelque chose pour faciliter ma vie, pas pour la compliquer davantage ..


0 commentaires

0
votes

Ma solution est d'utiliser une fonction comme mon coordinateur au lieu d'une classe. De cette façon, je n'ai aucun problème de propriété. Lorsque vous appuyez sur le bouton de retour, les vues du contrôleur de vue émettent des événements terminés et tout se déroule naturellement sans effort de ma part.

Le start () que vous montrez dans votre exemple peut être exprimé beaucoup plus simplement en:

func startInStore(navigationController: UINavigationController) {
    let inStoreMainViewModel = InStoreMainViewModel()
    let inStoreMainController = InStoreMainController()
    inStoreMainController.viewModel = inStoreMainViewModel

    navigationController.pushViewController(inStoreMainController, animated: true)
}

Un exemple d'application utilisant ce style peut être trouvé ici: https://github.com/danielt1263/RxMyCoordinator


0 commentaires