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

Créer un composant

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

Créer un composant

Créer des composants réutilisables

Lorsqu’on développe une application en SwiftUI, structurer son code de manière propre et organisé est essentiel. Créer des composants réutilisables permet d’éviter la duplication de code, d’améliorer la lisibilité et de rendre l’interface plus modulaire.

Un composant, c’est une vue que tu peux réutiliser plusieurs fois dans différentes parties de ton application. Il s’agit d’une struct indépendante, capable de recevoir des paramètres et de gérer son propre état. (Si tu n’es pas encore à l’aise avec les structures, je te remets un lien vers le cours, c’est essentiel de bien les maîtriser à partir d’ici.)

L’intérêt ? Gagner du temps en évitant de répéter du code et faciliter l’évolution de l’interface sans devoir tout modifier à la main. Plus propre, plus efficace !

La capture ci-dessus montre un bouton avec plusieurs paramètres, un nom, une icône, une couleur d’icône et une couleur de fond.

Si tu crées ces boutons un par un dans ton application et que tu veux les modifier plus tard, ça risque d’être un vrai casse-tête. Par contre, si tu prends le temps de créer un seul bouton réutilisable, avec des propriétés et méthodes bien pensées, tu pourras l’appeler partout où tu en as besoin sans te fatiguer.

Si tu veux modifier ton bouton, tu n’auras qu’un seul fichier à mettre à jour, et toute ton application s’adaptera automatiquement. Pratique, non ?

Cette notion sera divisée en deux parties. Dans cette première section, nous verrons comment créer un composant et pourquoi c’est une bonne pratique, avec des exemples concrets. Dans la seconde partie, nous découvrirons un nouveau property wrapper, @Binding, qui nous sera particulièrement utile dans certains cas.

Dupliquer sont code VS Isoler une vue

Pour bien comprendre pourquoi il est important d’isoler nos composants, on va d’abord voir ce qu’il ne faut pas faire.

Prenons l’exemple d’une liste de sports. Chaque ligne est représentée par un HStack, contenant une icône SF Symbols et un texte pour afficher le nom du sport. Si on ne pense pas à la réutilisation, on va se retrouver à copier-coller le même code encore et encore pour chaque sport… et c’est exactement ce qu’on veut éviter.


struct SportListView: View {
    var body: some View {
        VStack(alignment: .leading){
            HStack{
                Image(systemName: "figure.american.football")
                Text("Football americain")
            }
            HStack{
                Image(systemName: "figure.skiing.downhill")
                Text("Ski alpin")
            }
            HStack{
                Image(systemName: "figure.basketball")
                Text("Basket Ball")
            }
            HStack{
                Image(systemName: "figure.boxing")
                Text("Boxe anglaise")
            }
            HStack{
                Image(systemName: "figure.baseball")
                Text("Base Ball")
            }
        }
    }
}

Oups ! J’ai oublié un détail… je voulais mettre le nom des sports en gras. Du coup, me voilà obligé d’ajouter .fontWeight(.bold) sur chaque ligne… Relou !

struct struct SportListView: View {
    var body: some View {
        VStack(alignment: .leading){
            HStack{
                Image(systemName: "figure.american.football")
                Text("Football americain")
                    .fontWeight(.bold)
            }
            HStack{
                Image(systemName: "figure.skiing.downhill")
                Text("Ski alpin")
                    .fontWeight(.bold)
            }
            HStack{
                Image(systemName: "figure.basketball")
                Text("Basket Ball")
                    .fontWeight(.bold)
            }
            HStack{
                Image(systemName: "figure.boxing")
                Text("Boxe anglaise")
                    .fontWeight(.bold)
            }
            HStack{
                Image(systemName: "figure.baseball")
                Text("Base Ball")
                    .fontWeight(.bold)
            }
        }
    }
}

La solution, créer un composant réutilisable

La solution, c’est donc de créer une vue dédiée à ce besoin. (On comprend mieux pourquoi en SwiftUI, tout est une View !)

L’idée est de créer une vue réutilisable que je pourrai appeler partout dans mon application. Pour cela, je vais ajouter une nouvelle View. (Tu peux le faire avec le + en bas à gauche de Xcode en sélectionnant “New file from template”, ou plus rapidement avec le raccourci Cmd + N.)

Pense à bien nommer ta vue, son nom doit être clair et explicite. Ici, on veut afficher une ligne représentant un sport, et comme il s’agit d’une vue, je vais l’appeler SportRowView.

SportRow décrit le contenu de cette vue.

View indique qu’il s’agit d’une structure de vue.

Pourquoi c’est important ? Dans tes futurs projets, tu auras différents types de fichiers (Model, View, ViewModel…), et un bon nommage te permettra de t’y retrouver facilement en un coup d’œil.

Bref, revenons à notre exemple ! Dans SportRowView, je place la vue que je veux isoler. Ensuite, je n’ai plus qu’à appeler SportRowView partout où j’en ai besoin, notamment dans SportListView.

Petit rappel sur les structures, Quand je dis appeler SportRowView, je veux en réalité dire instancier SportRowView. Je créer des instances d’un modèle de vue, que je peux réutiliser autant que nécessaire.

struct struct SportRowView: View {
    var body: some View {
        HStack{
            Image(systemName: "figure.american.football")
            Text("Football americain")
                .fontWeight(.bold)
        }
    }
}

struct SportListView: View {
    var body: some View {
        VStack(alignment: .leading){
            SportRowView()
            SportRowView()
            SportRowView()
            SportRowView()
            SportRowView()
        }
    }
}

Customiser mon composant

C’est chouette, j’ai pu récupérer mon composant dans une autre vue, sauf que dans mon exemple, je me retrouve avec cinq fois la même ligne, avec la même couleur et les mêmes données…

Pour régler ce problème, c’est simple, mon composant SportRowView est une structure, et une structure sert à modéliser un objet. C’est exactement ce qu’on va faire ici.

struct struct SportRowView: View {
    var iconeSport: String
    var nameSport: String
    var color: Color
    
    var body: some View {
        HStack{
            Image(systemName: iconeSport)
            Text(nameSport)
                .fontWeight(.bold)
        }
        .foregroundStyle(color)
    }
}


#Preview {
    SportRowView(iconeSport: "figure.outdoor.cycle", nameSport: "Cyclisme", color: .green)
}

Au lieu de mettre des valeurs en dur dans mon composant, j’ai créé trois propriétés que j’ai typées. Ensuite, je les ai intégrées aux endroits où elles seront utilisées (dans mon exemple, pour l’image, le texte et la couleur).

Enfin, comme la preview utilise une instance de la View pour l’afficher dans le canvas, je dois lui fournir des données d’exemple pour qu’elle s’affiche correctement.

Tu peux maintenant instancier cette vue autant de fois que nécessaire dans les autres vues.

Ici, on en profite pour restructurer la vue initiale en utilisant notre composant réutilisable. C’est bien plus lisible, non ?

struct struct SportListView: View {
    var body: some View {
        VStack(alignment: .leading){
            SportRowView(iconeSport: "figure.american.football", nameSport: "Football americain", color: .red)
            SportRowView(iconeSport: "figure.skiing.downhill", nameSport: "Ski alpin", color: .blue)
            SportRowView(iconeSport: "figure.basketball", nameSport: "Basket Ball", color: .brown)
            SportRowView(iconeSport: "figure.boxing", nameSport: "Boxe anglaise", color: .orange)
            SportRowView(iconeSport: "figure.baseball", nameSport: "Base Ball", color: .purple)
        }
    }
}

Pour bien s’assurer que tu as compris l’intérêt de ce qu’on vient de faire, on va augmenter la taille des icônes.

Et maintenant, c’est ultra simple, il me suffit d’aller modifier mon composant, d’augmenter la taille, et toutes les instances de cette vue dans mon application seront mises à jour automatiquement.

 

struct struct SportRowView: View {
    var iconeSport: String
    var nameSport: String
    var color: Color
    
    var body: some View {
        HStack{
            Image(systemName: iconeSport)
                .font(.system(size: 32))
            Text(nameSport)
                .fontWeight(.bold)
        }
        .foregroundStyle(color)
    }
}

Dans l’exemple présenté dans ce cours, je travaille sur une seule vue pour une meilleure lisibilité.

Mais bien sûr, tout ce qu’on vient de faire impactera automatiquement toutes les instances du composant SportRowView présentes dans ton projet.

 

 

Tips

Dans ce cours, on a vu comment créer un composant. Comme tu l’as remarqué, on a créé un fichier dédié à ce composant, et en le nommant, Xcode a automatiquement généré une structure portant le même nom.

Pense à bien mettre une majuscule sur le nom du fichier pour respecter la convention PascalCase.

Autre point important,  un fichier = une structure. Si tu dois créer plusieurs vues, évite de les mettre dans un seul fichier, sinon tu risques de galérer à t’y retrouver quand ton projet prendra de l’ampleur.

Tips

Dans ce cours, on a vu comment créer un composant. Comme tu l’as remarqué, on a créé un fichier dédié à ce composant, et en le nommant, Xcode a automatiquement généré une structure portant le même nom.

Pense à bien mettre une majuscule sur le nom du fichier pour respecter la convention PascalCase.

Autre point important,  un fichier = une structure. Si tu dois créer plusieurs vues, évite de les mettre dans un seul fichier, sinon tu risques de galérer à t’y retrouver quand ton projet prendra de l’ampleur.