Navigation entre les vues à l'aide de @EnvironmentObject dans SwiftUI

La traduction de l'article a été préparée à la veille du lancement du cours avancé "iOS Developer" .




Bonjour et bienvenue dans notre tutoriel! Dans cette série, nous expliquons comment naviguer entre les vues dans SwiftUI (sans utiliser une vue de navigation!). Bien que cette idée puisse sembler banale, mais en la comprenant un peu plus profondément, nous pouvons en apprendre beaucoup sur les concepts de flux de données utilisés dans SwiftUI.

Dans la partie précédente, nous avons appris à implémenter cela en utilisant @ObservableObject. Dans cette partie, nous verrons comment faire de même, mais plus efficacement en utilisant @EnvironmentObject. Nous allons également ajouter une petite animation de transition.

Voici ce que nous allons réaliser:


Ce que nous avons


Donc, nous venons de comprendre comment naviguer entre différentes vues à l'aide d'ObservableObject. En bref, nous avons créé un ViewRouter et l'avons associé à Mother View et à notre Content View. Ensuite, nous manipulons simplement la propriété CurrentPage ViewRouter en cliquant sur les boutons Content View. Après cela, MotherView est mis à jour pour afficher la vue de contenu correspondante!

Mais il existe un deuxième moyen plus efficace pour atteindre cette fonctionnalité: utiliser @EnvironmentObject!

Astuce: Vous pouvez télécharger les derniers développements ici (c'est le dossier «NavigateInSwiftUIComplete») : GitHub

Pourquoi l'utilisation ObservableObjectn'est pas la meilleure solution


Vous vous demandez probablement: pourquoi devrions-nous implémenter cela d'une autre manière, alors que nous avons déjà une solution de travail? Eh bien, si vous regardez la logique de la hiérarchie de notre application, cela deviendra clair pour vous. Notre MotherView est la vue racine qui initialise l'instance de ViewRouter. Dans MotherView, nous initialisons également ContentViewA et ContentViewB, en leur passant l'instance ViewRouter en tant que BindableObject.

Comme vous pouvez le voir, nous devons suivre une hiérarchie stricte qui transmet un ObservableObject initialisé en aval à toutes les sous-vues. Maintenant, ce n'est pas si important, mais imaginez une application plus complexe avec beaucoup de vues. Nous devons toujours garder une trace de la transmission de la vue racine observable initialisée à toutes les sous-vues et toutes les sous-vues des sous-vues, etc., ce qui peut finalement devenir une tâche assez fastidieuse.



Pour résumer: l'utilisation de clean ObservableObjectpeut être problématique lorsqu'il s'agit de hiérarchies d'applications plus complexes.

Au lieu de cela, nous pourrions initialiser le ViewRouter au démarrage de l'application afin que toutes les vues puissent être directement connectées à cette instance, ou plutôt la regarder, quelle que soit la hiérarchie de l'application. Dans ce cas, l'instance de ViewRouter ressemblera à un nuage qui survole le code de notre application, auquel toutes les vues accèdent automatiquement sans se soucier de la chaîne d'initialisation correcte dans la hiérarchie des vues.
C'est juste le boulot EnvironmentObject!

Qu'est-ce que c'est EnvironmentObject?


EnvironmentObjectEst un modèle de données qui, après l'initialisation, peut échanger des données avec toutes les représentations de votre application. Ce qui est particulièrement bon, c'est ce qui est EnvironmentObjectcréé en fournissant ObservableObject, afin que nous puissions utiliser le nôtre ViewRouterpour créer EnvironmentObject!

Dès que nous nous déclarions ViewRouterque EnvironmentObject, toutes les représentations peuvent être attachées à de la même manière que pour les ordinaires ObservableObject, mais sans la nécessité d'une chaîne de initialisations dans la hiérarchie de l' application!

Comme déjà mentionné, il EnvironmentObjectdoit déjà être initialisé lors du premier accès. Étant donné que la nôtre MotherView, en tant que vue racine, examinera la propriété CurrentPage ViewRouter, nous devons l'initialiser au EnvironmentObjectdémarrage de l'application. Ensuite, nous pouvons automatiquement changer currentPageEnvironmentObjectde ContentView, ce qui appelle alors MotherViewun nouveau rendu.



Mise ViewRouteren œuvre en tant queEnvironmentObject


Alors, mettons à jour le code de notre application!

Tout d'abord, changez le wrapper de la propriété à l' viewRouterintérieur MotherViewde @ObservableObjecten @EnvironmentObject.

import SwiftUI

struct MotherView : View {
@EnvironmentObject var viewRouter: ViewRouter
var body: some View {
//...
    }
}

La propriété est viewRoutermaintenant à la recherche ViewRouter-EnvironmentObject. Ainsi, nous devons fournir à notre structure MotherView_Previewsl'instance appropriée:

#if DEBUG
struct MotherView_Previews : PreviewProvider {
    static var previews: some View {
        MotherView().environmentObject(ViewRouter())
    }
}
#endif

Comme mentionné ci-dessus - lors du démarrage de notre application, il devrait être immédiatement fourni avec une instance ViewRouterde qualité EnvironmentObject, car elle MotherViewfait maintenant référence à la représentation racine EnvironmentObject. Par conséquent, mettez à jour la fonction de scène à l'intérieur du fichier SceneDelegage.swiftcomme suit:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: MotherView().environmentObject(ViewRouter()))
            self.window = window
            window.makeKeyAndVisible()
        }
    }

Très bien, maintenant lorsque vous démarrez l'application, SwiftUI crée une instance ViewRouterde qualité EnvironmentObject, à laquelle toutes les vues de notre application peuvent maintenant être attachées.

Ensuite, mettons à jour le nôtre ContentViewA. Modifiez sa viewRouterpropriété en EnvironmentObjectet mettez également à jour la structure ContentViewA_Previews.

import SwiftUI

struct ContentViewA : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
       //...
    }
}
#if DEBUG
struct ContentViewA_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewA().environmentObject(ViewRouter())
    }
}
#endif

Astuce: Encore une fois, la structure ContentViewsA_Previewsa sa propre instance ViewRouter, mais ContentViewAest associée à l'instance créée lors du lancement de l'application!

Répétons ceci pour ContentViewB:

import SwiftUI

struct ContentViewB : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View {
        //...
    }
}
#if DEBUG
struct ContentViewB_Previews : PreviewProvider {
    static var previews: some View {
        ContentViewB().environmentObject(ViewRouter())
    }
}
#endif

Puisque viewRouternos propriétés sont ContentViewmaintenant directement liées à / observons l'instance initiale ViewRouteras EnvironmentObject, nous n'avons plus besoin de les initialiser dans la nôtre MotherView. Alors, mettons à jour notre MotherView:

struct MotherView : View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    
    var body: some View { 
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
            }
        }
    }
}

C'est tout simplement génial: nous n'avons plus besoin d'initialiser à l' ViewRouterintérieur de la nôtre MotherViewet de transmettre son instance à ContentView, ce qui peut être très efficace, en particulier pour les hiérarchies plus complexes.

Très bien, exécutons notre application et voyons comment cela fonctionne.


Génial, nous pouvons toujours nous déplacer entre nos vues!

Ajout d'animations de transition


En bonus, voyons comment ajouter une animation de transition lors du passage de «page1» à «page2».

Dans SwiftUI, c'est assez simple.
Regardez la willChangeméthode que nous appelons le fichier ViewRouter.swiftlors de la CurrentPagemise à jour. Comme vous le savez déjà, cela provoque une reliure MotherViewpour réafficher votre corps, en montrant finalement un autre ContentView, ce qui signifie passer à un autre ContentView. Nous pouvons ajouter une animation en enveloppant simplement la méthode willChangedans une fonction withAnimation:

var currentPage: String = "page1" {
        didSet {
            withAnimation() {
                willChange.send(self)
            }
        }
    }

Nous pouvons maintenant ajouter une animation de transition lors de l'affichage d'une autre Content View.
"WithAnimation (_: _ :) - renvoie le résultat du recalcul du corps de la vue avec l'animation fournie"
Apple
Nous voulons fournir à notre application une transition contextuelle lors de la navigation de ContentViewAà ContentViewB. Pour ce faire, accédez au fichier MotherView.swiftet ajoutez le modificateur de transition lors de l'appel ContentViewB. Vous pouvez choisir l'un des nombreux types de transition prédéfinis ou même créer le vôtre (mais c'est un sujet pour un autre article). Pour ajouter une transition contextuelle, nous sélectionnons un type .scale.

var body: some View {
        VStack {
            if viewRouter.currentPage == "page1" {
                ContentViewA()
            } else if viewRouter.currentPage == "page2" {
                ContentViewB()
                    .transition(.scale)
            }
        }
    }

Pour voir comment cela fonctionne, exécutez votre application dans un simulateur ordinaire:


Cool, nous avons ajouté une belle animation de transition à notre application en quelques lignes de code!

Vous pouvez télécharger tout le code source ici !

Conclusion


C'est tout! Nous avons appris pourquoi il est préférable d'utiliser EnvironmentObjectpour naviguer entre les vues dans SwiftUI et comment l'implémenter. Nous avons également appris à ajouter des animations de transition à la navigation. Si vous voulez en savoir plus, suivez-nous sur Instagram et abonnez-vous à notre newsletter pour ne pas manquer les mises à jour, tutoriels et astuces sur SwiftUI et bien plus encore!



Leçon gratuite: «Accélérer les applications iOS avec des instruments»



All Articles