Image de présentation d’un écran iOS illustrant les shapes en SwiftUI, plusieurs forme de différentes couleurs sont présentes

Navigation

Image de présentation d’un écran iOS illustrant les shapes en SwiftUI, plusieurs forme de différentes couleurs sont présentes

Navigation

On attaque l’ultime arc de notions dans cette formation sur les bases de SwiftUI, la navigation.

Depuis le début, on s’éclate à créer nos écrans, c’est chouette, mais qu’est-ce qu’une application sans navigation ? Ça existe, mais rares sont celles qui n’en ont pas.

Je parle d’arc de notions parce qu’on va voir plusieurs outils autour de la navigation. Pour commencer, je vais te présenter la navigation hiérarchique, la plus classique, c’est le passage d’un écran parent vers un écran enfant. Par exemple, une liste de recettes de cuisine où l’utilisateur appuie sur une recette pour afficher l’écran de détail correspondant.

Ensuite, on verra les modales (oui, ce sont des pop-ups, mais on est des pros, donc on dit “modale” ! 😎). On abordera aussi la toolbar, qui permet d’ajouter des boutons interactifs dans la barre de navigation.

Enfin, on terminera avec la célèbre TabView, ce fameux menu en bas de l’écran, omniprésent dans nos applications mobiles.

C’est le sprint final de cette formation, alors accroche-toi, tu es presque au bout !

 

NavigationStack

Pour commencer avec la navigation, je te présente une nouvelle stack, NavigationStack. C’est un composant qui permet de déclarer une pile de navigation.

Pour être clair, la vue sur laquelle tu places une NavigationStack sera ta vue parent. La NavigationStack sera toujours le conteneur principal de ta navigation.

let struct MyNavView: View {
    var body: some View {
        NavigationStack {
            Text("Vue parente")
    }
}

La pile de navigation est ici correctement déclaré.

 Modificateurs du NavigationStack

Le NavigationStack vient avec une série de modificateurs propres à son utilisation. Par exemple, le modificateur navigationTitle, qui permet d’ajouter un titre à l’écran sur lequel il se trouve. Ce modificateur est d’ailleurs particulier, car contrairement à d’autres modificateurs qui se placent à la fin des accolades du composant, celui-ci se place à l’intérieur des accolades du contenu du NavigationStack, comme le montre l’exemple ci-dessous.

struct MyNavView: View {
    var body: some View {
        NavigationStack {
            Text("Hello World!")
                .navigationTitle("Vue parent")
        }
    }
}

En plus de navigationTitle, le NavigationStack vient avec de nombreux modificateurs spécifiques à son utilisation. Je te mets la liste ici, et nous en verrons certains plus en détail, notamment .navigationDestination et .toolbar.

Modificateur Fonction
.navigationTitle(_:) Définit le titre en haut de l’écran
.navigationBarTitleDisplayMode(_:)
Change la taille du titre (large, inline ou automatic)
.navigationDestination(for:)
.navigationBarTitleDisplayMode(_:)
.navigationBarBackButtonHidden(_:) Cache le bouton retour
.toolbar(_:) Ajoute des boutons interactifs dans la barre de navigation


struct MyNavView: View {
    var body: some View {
        NavigationStack {
            VStack {
                NavigationLink {
                    ChildView()
                } label: {
                    ZStack {
                        Rectangle()
                            .fill(Color.orange)
                            .cornerRadius(10)
                            .frame(width: 200, height: 50)
                        HStack {
                            Text("Vue enfant")
                                .font(.headline)
                                .fontWeight(.bold)
                            Image(systemName: "chevron.right")
                        }
                        .foregroundStyle(.white)
                    }
                }
            }
            .navigationTitle("Vue parent")
        }
    }
}

En appuyant sur le NavigationLink, l’utilisateur est automatiquement dirigé vers l’écran enfant placé dans la destination (ici ChildView).

La destination, c’est la vue où tu veux que ton utilisateur arrive après avoir déclenché un NavigationLink.

Tu observeras que le bouton de retour est ajouté automatiquement dans le processus de navigation. Par défaut, il prend le nom du titre de navigation (navigationTitle). Si aucun titre de navigation n’est défini, il s’appellera simplement “Back”.

Faire transiter une donnée d’un écran à un autre.


struct AppleDevice: Identifiable {
    let id = UUID()
    let name: String
    let icon: String
    let category: String
}

let appleDevices: [AppleDevice] = [
    AppleDevice(name: "iPhone 15 Pro", icon: "iphone", category: "phone"),
    AppleDevice(name: "iPad Pro", icon: "ipad", category: "tablet"),
    AppleDevice(name: "MacBook Air M2", icon: "laptopcomputer", category: "laptop"),
    AppleDevice(name: "Apple Watch Series 9", icon: "applewatch", category: "wearable"),
    AppleDevice(name: "AirPods Pro 2", icon: "airpodspro", category: "audio"),
    AppleDevice(name: "Apple TV 4K", icon: "appletv", category: "home"),
    AppleDevice(name: "Apple Vision Pro", icon: "visionpro", category: "vision"),
    AppleDevice(name: "iPhone SE", icon: "iphone.gen2", category: "phone"),
    AppleDevice(name: "MacBook Pro M3", icon: "laptopcomputer", category: "laptop"),
    AppleDevice(name: "iPad Mini", icon: "ipad", category: "tablet")
]
J’ai créé un type AppleDevice et un tableau contenant tous les appareils d’Apple que je souhaite lister dans mon application. Pour bien comprendre la transition de données, on va d’abord analyser la vue enfant. C’est ici que l’on va réceptionner la donnée sélectionnée depuis la liste. Ce que l’on veut afficher, c’est le type de donnée de mes appareils, donc AppleDevice. J’ai stocké le type AppleDevice dans une propriété device, ce qui me permet de récupérer les différentes propriétés de mon type.

struct AppleDeviceDetailView: View {
    let device: AppleDevice

    var body: some View {
        VStack(spacing: 20) {
            Image(systemName: device.icon)
                .resizable()
                .scaledToFit()
                .frame(width: 100, height: 100)
            Text(device.name)
                .font(.largeTitle)
                .fontWeight(.bold)
            
            Text("Catégorie : \(device.category)")
                .font(.title3)
                .foregroundColor(.gray)
        }
        .padding()
        .navigationTitle(device.name)
    }
}

#Preview {
    AppleDeviceDetailView(device: AppleDevice(name: "iPhone 15 Pro", icon: "iphone", category: "phone"))
}
Dans la vue parent, j’ai une liste qui boucle sur mon tableau appleDevices. Dans la destination du NavigationLink, je lui demande de se diriger vers la vue enfant que l’on vient de voir ci-dessus : AppleDeviceDetailView. La destination n’est autre qu’une instance de cette vue. Comme AppleDeviceDetailView possède une propriété device, il faut lui fournir une valeur correspondante. Pour rappel, device représente un élément du type AppleDevice, donc je dois lui envoyer une donnée de ce type. Ici, je lui fournis l’élément de la liste appleDevices correspondant à l’index de la boucle, ce qui représente chaque instance du tableau. Maintenant, c’est fonctionnel, si l’utilisateur appuie sur l’élément à l’index 3, il verra les détails de cet appareil dans l’écran enfant. Si tu as compris ce qu’on vient d’expliquer, tu as compris le fonctionnement de la navigation avec passage de données ! Sinon, prends le temps de bien décortiquer le code, promis, ce n’est pas si compliqué que ça !

struct AppleDeviceListView: View {
    var body: some View {
        NavigationStack {
            List(appleDevices) { index in
                NavigationLink(destination: AppleDeviceDetailView(device: index)) {
                    HStack(spacing: 15) {
                        Image(systemName: index.icon)
                            .font(.title)
                        VStack(alignment: .leading) {
                            Text(index.name)
                                .font(.headline)
                            Text(index.category)
                                .font(.subheadline)
                                .foregroundColor(.gray)
                        }
                        Image(systemName: "chevron.right")
                            .foregroundColor(.gray)
                    }
                    .padding(.vertical, 10)
                }
            }
            .navigationTitle("Appareils Apple")
        }
    }
}

Tips

Parlons un peu d’UX (User Experience)

La navigation est un pilier essentiel de tes applications. C’est un élément qui doit être simple à comprendre et donc intuitif pour l’utilisateur. Quand tu conçois une app, pense toujours UX, ton application doit résoudre un problème, pas en créer de nouveaux (comme être perdu dans l’interface, coucou le site de la CAF 😄).

Les règles de base en navigation sont relativement simples. Les boutons de navigation doivent être clairs et facilement identifiables, avec une couleur et un format homogènes dans toute l’application. Chaque écran enfant doit toujours offrir un moyen de revenir soit à l’écran précédent, soit à un endroit clé de l’app. Ne bloque jamais un utilisateur sur une impasse de navigation.

Ça demande une vraie réflexion. L’UX est omniprésente dans notre métier, et si tu veux créer des apps de qualité, il faudra s’y intéresser. Même si tu fais une app pour toi-même, une bonne expérience utilisateur fera toute la différence pour en faire un projet vraiment réussi !

Tips

Parlons un peu d’UX (User Experience)

La navigation est un pilier essentiel de tes applications. C’est un élément qui doit être simple à comprendre et donc intuitif pour l’utilisateur. Quand tu conçois une app, pense toujours UX, ton application doit résoudre un problème, pas en créer de nouveaux (comme être perdu dans l’interface, coucou le site de la CAF 😄).

Les règles de base en navigation sont relativement simples. Les boutons de navigation doivent être clairs et facilement identifiables, avec une couleur et un format homogènes dans toute l’application. Chaque écran enfant doit toujours offrir un moyen de revenir soit à l’écran précédent, soit à un endroit clé de l’app. Ne bloque jamais un utilisateur sur une impasse de navigation.

Ça demande une vraie réflexion. L’UX est omniprésente dans notre métier, et si tu veux créer des apps de qualité, il faudra s’y intéresser. Même si tu fais une app pour toi-même, une bonne expérience utilisateur fera toute la différence pour en faire un projet vraiment réussi !