Dans la première partie de cet article nous avions commencé à intégrer une interface simple via le framework SwiftUI. Aujourd’hui nous allons le terminer. Pour cela nous allons découvrir de nouvelles propriétés bien pratiques. Trêve de bavardages, allons-y !

Reprenons le chantier

Nous allons commencer la partie basse de notre template. Pour cela nous allons utiliser une VStack sur laquelle nous allons mettre un padding, un backgroundColor en « darkBg » (que nous avions déjà ajouté) et un cornerRadius de 30. Pour que notre stack prenne tout l’espace nous pouvons ici tricher un peu en insérant une HStack avec un Spacer dedans. Cela permettra sans avoir à set la width de forcer notre VStack à prendre tout l’espace. Et pour ne pas avoir mis une HStack pour rien, insérons un Text juste au dessus du Spacer pour servir de titre ! Le contenu sera « Categories », on mettra le texte en bold, avec un padding vertical et une taille de texte à 20px.

VStack {
    HStack() {
        Text("Categories")
            .bold()
            .padding(.vertical)
            .font(.system(size: 20))
        Spacer()
    }
}
.padding(20)
.background(Color("darkBg"))
.cornerRadius(30)

Pour remplir ce conteneur, nous allons nous occuper de notre composant « Skill » c’est à dire la partie avec une icone, un titre et une barre de progression. Ainsi vu qu’il s’agit d’un composant réutilisable nous pourrons lui passer l’icone, la couleur, le titre et le pourcentage à afficher.

Le component Skill

Component Skill en SwiftUI

Commençons notre structure avec simplement nos variables à récupérer en props:

import SwiftUI

struct Skill: View {
    var title: String;
    var icon: String;
    var percent: Int;
    var color: Color;
    var body: some View {}
}

struct Skill_Previews: PreviewProvider {
    static var previews: some View {
        Skill(title: "Design", icon: "square.stack.3d.up.fill", percent: 20, color: .purple)
    }
}

Notons qu’ici dans la partie Preview j’utilise pour icon un nom récupéré depuis Symbols par Apple. Nous verrons comment l’utiliser par la suite, mais si vous préférez vous pouvez plutôt choisir de passer par une vraie image (puisque nous utiliserons un component Image de toutes façons).

Commençons donc par l’icone, comme nous la voulons contenue dans un cercle coloré, nous allons ajouter un VStack en 50px * 50px. Nous prendrons en background la couleur envoyée en props, et pour en faire un rond nous ajouterons simplement un cornerRadius de 25px. Dedans, nous pouvons tout simplement ajouter une Image avec comme systemName le nom d’icone passé en props.

VStack {
    Image(systemName: icon)
}
.frame(width: 50, height: 50)
.background(color)
.cornerRadius(25)

Pour la partie droite, nous allons créer une VStack qui contiendra deux HStack: une pour le texte et une pour la barre de progression. Pour celle du haut, rien de compliqué: deux textes (un en bold, l’autre en light et en font-size 14) séparés par un espace. Simple petite chose à penser: venir transformer notre pourcentage en string pour le concaténer au reste:

VStack {
    HStack {
        Text(title)
            .bold()
        Spacer()
        Text(String(percent) + "% spent")
            .fontWeight(.light)
            .font(.system(size: 14))
    }
    // La barre de progression ira ici
}
.padding(.horizontal)

Notons que par pur esthétisme, j’ai ajouté un padding horizontal à notre conteneur principal. Et c’est là que les choses se compliquent: comment faire en sorte que la barre de progression fasse la bonne taille ? Impossible ici de simplement mettre l’unité de mesure comme en CSS. Nous allons donc devoir passer par un GeometryReader.

La barre de progression

Le GeometryReader permet de déclarer un container dont on peut récupérer la taille et la position, ce qui peut être très pratique pour ce style de manipulations. Pour obtenir la taille souhaitée, nous devons récupérer la largeur totale de notre barre, la multiplier par notre pourcentage récupéré en props (que nous allons transformer en CGFloat pour le calcul) puis diviser par 100. Nous allons définir une hauteur fixe de 12px. Nous mettrons une couleur grise pour le fond, et la couleur envoyée en props pour la progression, ce qui nous donne:

HStack {
    GeometryReader { geometry in
        HStack {}
            .frame(width: geometry.size.width * CGFloat(percent) / 100, height: 12)
            .background(color)
    }
    .frame(height: 12)
}
.background(Color(.lightGray))
.cornerRadius(3)

Voici donc le code complet de notre component Skill:

import SwiftUI

struct Skill: View {
    var title: String;
    var icon: String;
    var percent: Int;
    var color: Color;
    var body: some View {
        HStack {
            VStack {
                Image(systemName: icon)
            }
            .frame(width: 50, height: 50)
            .background(color)
            .cornerRadius(25)
            VStack {
                HStack {
                    Text(title)
                        .bold()
                    Spacer()
                    Text(String(percent) + "% spent")
                        .fontWeight(.light)
                        .font(.system(size: 14))
                }
                HStack {
                    GeometryReader { geometry in
                        HStack {}
                            .frame(width: geometry.size.width * CGFloat(percent) / 100, height: 12)
                            .background(color)
                    }
                    .frame(height: 12)
                }
                .background(Color(.lightGray))
                .cornerRadius(3)
            }
            .padding(.horizontal)
        }
    }
}

Afficher notre liste de skills

Pour l’afficher nous n’allons pas faire comme la dernière fois. Je vous avais promis de voir de nouvelles fonctions de SwiftUI, et si vous avez l’habitude de développer des interfaces front, il y en a une que vous attendez sûrement: faire une boucle ! Nous allons donc dans notre vue principale définir un array de Skills à afficher, comme si on l’avait récupéré depuis une base:

let skills = [
    (title: "Design", icon: "square.stack.3d.up.fill", percent: 74, color: Color.purple),
    (title: "Communication", icon: "quote.bubble.fill", percent: 52, color: Color.green),
]

Ensuite, à l’endroit où nous souhaitons les afficher (sous le titre « Catégories » ajouté en début d’article) nous allons appeler la fonction Foreach avec deux arguments: notre array et une clé unique pour chaque élément du tableau. Pour nous simplifier la vie, nous utiliserons ici le titre de la catégorie. Dans le scope de notre boucle, nous avons désormais accès à une variable skill qui contient notre objet. Il ne reste plus qu’à appeler le component:

ForEach(skills, id: \.title) { skill in
    Skill(
        title: skill.title,
        icon: skill.icon,
        percent: skill.percent,
        color: skill.color
    )
    .padding(.vertical, 8)
}

J’ai simplement ajouté un padding pour les séparer un peu, et vous pouvez désormais admirer le rendu ! Notre projet est presque terminé, nous verrons la fin de ce template dans la partie 3 de ce tutoriel sur comment débuter avec SwiftUI.

Si vous avez besoin de compétences mobile (iOS/Android) pour votre projet, n’hésitez pas à entrer en contact avec moi pour que nous puissions en discuter.