SwiftUI : naviguer entre vues

Par ybenkhayat, 1 mars, 2025
SwiftUI logo

Retour et plan

Dans le blogue précédent, nous avons vu les différents types de Stack pour créer des mises en pages complexes. Dans le blogue qui suit, nous allons aborder la navigation dans SwiftUI. Il faut savoir qu’il y a plusieurs façons pour naviguer entre des vues avec SwiftUI. Dans ce blogue, nous allons principalement voir comment naviguer de vue en vue, avec NavigationView et NavigationStack. Comme dans les blogues précédents, toutes les manipulations et le code produit en lien avec ce blogue sont disponibles dans ce dépôt GitHub.

NavigationView

NavigationView agit comme un contrôleur, englobant les vues disponibles et permettant le changement entre celles-ci. Cette vue permet de traverser une collection de vues avec des NavigationLink (qu’on doit définir). ¹ ²

Démonstration

Pour cette démonstration de ce blogue, nous allons réutiliser le même fichier ContentView du blogue précédent et effacer ce qu’il y avait. Nous allons donc créer une nouvelle vue appelée ChatonView. Dans cette structure, nous allons avoir un ZStack avec un cercle qui servira de fond circulaire, puis par-dessus, une image d’un chaton. On obtient ainsi le code suivant :

import SwiftUI

struct ContentView: View {
    var body: some View {
        ChatonView(couleur: Color(hue: 0.516, saturation: 1.0, brightness: 1.0))
        
    }
}

struct ChatonView: View {
    var couleur : Color
    
    var body: some View{
        ZStack {
            Circle()
                .frame(width: 300, height: 300)
                .foregroundColor(couleur)
            Image("kitten2")
                .resizable()
                .scaledToFit()
                .frame(width: 250, height: 250)
                .clipShape(.circle)
        }
    }
}

#Preview {
    ContentView()
}

On ajoutera ensuite un NavigationView qui englobera le ChatonView qu’on venait de créer ainsi qu’un NavigationTitle pour donner un titre à notre vue et qui sera affiché en haut à gauche de la vue:

struct ContentView: View {
    var body: some View {
        NavigationView{
            ChatonView(couleur: Color(hue: 0.516, saturation: 1.0, brightness: 1.0))
                .navigationTitle("Vue 1 : Chaton")
	            .offset(y: -60)
        }
    }
}

Ensuite, nous ajouterons un NavigationLink, permettant de changer de la vue originale du Chaton à une nouvelle (présentement une qui est simplement un placeholder). Après avoir ajouté le NavigationLink, vous devriez avoir un bouton « Vue suivante » en dessous du chaton. Lorsque l’on clique sur le nouveau bouton « Vue suivante », on devrait être redirigé dans une vue presque vide ayant seulement le texte « Destination » au centre. On obtient le code suivant :

struct ContentView: View {
    var body: some View {
        NavigationView{
            VStack{
                ChatonView(couleur: Color(hue: 0.516, saturation: 1.0, brightness: 1.0))
                    .navigationTitle("Vue 1 : Chaton")
                    .offset(y: -60)
                
                NavigationLink(destination: Text("Destination"), label: {
                    Text("Vue suivante")
                })
            }
        }
    }
}

On obtient le résultat suivant :

Alt Text

Maintenant, nous allons ajouter une vraie deuxième vue, comme ChatonVue, nommée ChienVue. Pour cela, nous allons remplacer la struct parent (ContentView) par ChatonVue et ensuite la dupliquer pour créer ChienVue. La vue ChatonVue originale sera nommée à VueCercleTemplate, puisqu’elle sera utilisée par les deux vues (ChatonVue et ChienVue) comme template. On obtiendra ainsi le code suivant :

import SwiftUI

struct ChatonVue: View {
    var body: some View {
        NavigationView{
            VStack{
                VueCercleTemplate(couleur: Color(hue: 0.516, saturation: 1.0, brightness: 1.0), nomImage: "kitten2")
                    .navigationTitle("Chaton")
                    .offset(y: -60)
                
                NavigationLink(destination: ChienVue(), label: {
                    Text("Chien")
                })
            }
        }
    }
}

struct ChienVue: View {
    var body: some View {
        VStack{
            VueCercleTemplate(couleur: Color .yellow, nomImage: "chien3")
                .navigationTitle("Chien")
                .offset(y: -60)
            
            NavigationLink(destination: Text("Destination"), label: {
                Text("Hamster")
            })
        }
    }
}

struct VueCercleTemplate: View {
    var couleur : Color
    var nomImage : String
    
    var body: some View{
        ZStack {
            Circle()
                .frame(width: 300, height: 300)
                .foregroundColor(couleur)
            Image(nomImage)
                .resizable()
                .scaledToFit()
                .frame(width: 250, height: 250)
                .clipShape(.circle)
        }
    }
}

#Preview {
    ChatonVue()
}

On obtient le résultat suivant :

Alt Text

Ensuite, on va finalement créer la troisième vue appelée HamsterVue. On va simplement dupliquer le code de ChienVue et modifier les attributs nomImage et couleur en fonction de la nouvelle vue affichant un hamster. On obtient ainsi le code suivant :

import SwiftUI

struct ChatonVue: View {
    var body: some View {
        NavigationView{
            VStack{
                VueCercleTemplate(couleur: Color(hue: 0.516, saturation: 1.0, brightness: 1.0), nomImage: "kitten2")
                    .navigationTitle("Chaton")
                    .offset(y: -60)
                
                NavigationLink(destination: ChienVue(), label: {
                    Text("Chien")
                        .bold().frame(width: 280, height: 50)
                        .background(Color .blue)
                        .foregroundColor(.white).cornerRadius(10)
                })
            }
        }.accentColor(Color(.label))
    }
}

struct ChienVue: View {
    var body: some View {
        VStack{
            VueCercleTemplate(couleur: Color .yellow, nomImage: "chien3")
                .navigationTitle("Chien")
                .offset(y: -60)
            
            NavigationLink(destination: HamsterVue(), label: {
                Text("Hamster")
                    .bold().frame(width: 280, height: 50)
                    .background(Color .orange)
                    .foregroundColor(.white).cornerRadius(10)
            })
        }
    }
}

struct HamsterVue: View {
    var body: some View {
        VStack{
            VueCercleTemplate(couleur: Color .gray, nomImage: "hamster2")
                .navigationTitle("Hamster")
                .offset(y: -60)
        }
    }
}

struct VueCercleTemplate: View {
    var couleur : Color
    var nomImage : String
    
    var body: some View{
        ZStack {
            Circle()
                .frame(width: 300, height: 300)
                .foregroundColor(couleur)
            Image(nomImage)
                .resizable()
                .scaledToFit()
                .frame(width: 250, height: 250)
                .clipShape(.circle)
        }
    }
}

#Preview {
    ChatonVue()
}

Et le résultat suivant :

Alt Text

NavigationStack

NavigationStack permet de créer une liste de vues par-dessus une vue racine. Cela permet d’éviter de définir chaque vue séparément (comme on l’a vu avec NavigationView). Lorsqu’on accède à une nouvelle vue, celle-ci est ajoutée au Stack et celui-ci affichera toujours la dernière vue qui n’a pas été supprimée. ³ ⁴

Démonstration

On réutilisera le même exemple que celui qui précède (avec les animaux chaton, chien et hamster). On Créera une liste d’animaux, et lorsque l’on clique sur chacun, une vue s’affichera. À noter que ces animaux seront des données, et non des vues. On créera donc un struct Animaux avec les propriétés nom, nomImage et couleur et une liste d’animaux où ils seront initialisés.

On débutera avec le code suivant :

import SwiftUI

struct ChatonVue: View {
    var animaux : [Animaux] = [.init(nom: "Chaton", couleur: .blue, nomImage: "kitten2"), .init(nom: "Chien", couleur: .yellow, nomImage: "chien3"), .init(nom: "Hamster", couleur: .gray, nomImage: "hamster2")]
    
    
    var body: some View {
        List {
            Section("Animaux") {
                ForEach(animaux, id: \.nom) {animal in
                    HStack{
                        Image(animal.nomImage).resizable().scaledToFit().frame(width: 50, height: 50).clipShape(.circle).overlay{
                            Circle().stroke(animal.couleur, lineWidth : 2)
                        }
                        Text(animal.nom)
                            .offset(x: 5).foregroundColor(animal.couleur)
                    }
                }
            }
        }
    }
}
    
    
struct Animaux {
    let nom: String
    let couleur : Color
    let nomImage : String
}

#Preview {
    ChatonVue()
}

Ce code devrait donner ce résultat :

alt text

On ajoutera un NavigationStack qui englobera la liste des animaux. On ajoutera aussi à la liste un navigationTitle qui sera affiché en haut à gauche. On obtient le code suivant :

var body: some View {
    NavigationStack{
        List {
            Section("Liste d'animaux") {
                ForEach(animaux, id: \.nom) {animal in
                    HStack{
                        Image(animal.nomImage).resizable().scaledToFit().frame(width: 50, height: 50).clipShape(.circle).overlay{
                            Circle().stroke(animal.couleur, lineWidth : 2)
                        }
                        Text(animal.nom)
                            .offset(x: 5).foregroundColor(animal.couleur)
                    }
                }
            }
        }.navigationTitle("Mes animaux")
    }
}

Ensuite, on ajoute la destination dans navigationDestination, qui est l’animal lui-même. On définit avec un ZStack l’apparence de la vue de destination, et puisque les animaux sont des données, on envoie seulement l’animal et ses attributs, et ceux-ci sont formatés automatiquement à l’aide de la vue prédéfinie. On obtient ainsi le code suivant :

import SwiftUI

struct ChatonVue: View {
    var animaux : [Animal] = [.init(nom: "Chaton", couleur: .blue, nomImage: "kitten2"), .init(nom: "Chien", couleur: .yellow, nomImage: "chien3"), .init(nom: "Hamster", couleur: .gray, nomImage: "hamster2")]
    
    
    var body: some View {
        NavigationStack{
            List {
                Section("Liste d'animaux") {
                    ForEach(animaux, id: \.nom) {animal in
                        NavigationLink(value: animal){
                            HStack{
                                Image(animal.nomImage).resizable().scaledToFit().frame(width: 50, height: 50).clipShape(.circle).overlay{
                                    Circle().stroke(animal.couleur, lineWidth : 2)
                                }
                                Text(animal.nom)
                                    .offset(x: 5).foregroundColor(animal.couleur)
                            }
                        }
                    }
                }
            }.navigationTitle("Mes animaux")
                .navigationDestination(for: Animal.self) { animal in
                    ZStack{
                        animal.couleur.ignoresSafeArea()
                        HStack{
                            Image(animal.nomImage).resizable().scaledToFit().frame(width: 100, height: 100).clipShape(.circle).overlay{
                                Circle().stroke(.black, lineWidth : 2)
                            }
                            Text(animal.nom)
                                .offset(x: 5).foregroundColor(.black).font(.largeTitle).bold()
                        }
                    }
                    
                }
        }.accentColor(Color(.label))
    }
}
    
    
struct Animal : Hashable{
    let nom: String
    let couleur : Color
    let nomImage : String
}

#Preview {
    ChatonVue()
}

Qui donne le résultat suivant :

Alt Text


  1. Developer Apple, « NavigationView », s.d., https://developer.apple.com/documentation/swiftui/navigationview

  2. Developer Apple, « NavigationLink », s.d., https://developer.apple.com/documentation/swiftui/navigationlink

  3. Logan KLEIN, « Navigation in SwiftUI 4: NavigationView, NavigationLink, NavigationStack, and NavigationSplitView », dans Big Nerd Ranch, 7 juin 2022, https://bignerdranch.com/blog/the-different-forms-of-navigation-in-swiftui/

  4. Developer Apple, « NavigationStack », s.d., https://developer.apple.com/documentation/swiftui/navigationstack

  5. Apple Developer, « WWDC24: SwiftUI essentials | Apple », dans YouTube, 10 juin 2024, https://www.youtube.com/watch?v=HyQgpxX__-A

  6. Sean ALLEN, « NavigationStack - SwiftUI Programmatic Navigation - iOS 16 », dans YouTube, 25 novembre 2022, https://www.youtube.com/watch?v=oxp8Qqwr4AY&t=332s

  7. Sean ALLEN, « SwiftUI Navigation - NavigationView & NavigationLink Tutorial », dans YouTube, 30 décembre 2020, https://www.youtube.com/watch?v=IopCl8sOyFA

Étiquettes

Commentaires