Implémentation avancée d’un indicateur circulaire de progression sur iOS avec Swift

Un indicateur circulaire de progression bien conçu améliore significativement la clarté et l’engagement utilisateur dans les applications iOS. Ce guide explore une bibliothèque moderne, écrite entièrement en Swift, conçue pour offrir un contrôle précis sur l’apparence, le comportement et les animations de ce type d’élément d’interface.

Présentation de la bibliothèque

La bibliothèque fournit une implémentation native de ProgressRing, une vue personnalisable qui s’intègre naturellement dans les projets SwiftUI ou UIKit. Elle ne dépend pas de couches graphiques externes ni de rendu basé sur Core Graphics manuel, ce qui garantit une performance fluide même lors de mises à jour fréquentes du pourcentage.

Intégration dans votre projet

Pour intégrer la bibliothèque via Swift Package Manager, ajoutez l’URL du dépôt dans Xcode (File > Add Packages…) :

https://github.com/gh-mirrors/ui/UICircularProgressRing.git

Vous pouvez également utiliser SwiftPM depuis un fichier Package.swift :

dependencies: [
    .package(url: "https://github.com/gh-mirrors/ui/UICircularProgressRing.git", from: "8.0.0")
]

Création d’un indicateur de base

L’initialisation se fait avec une valeur normalisée entre 0 et 1, accompagnée de deux styles distincts pour les anneaux intérieur et extérieur :

let ring = ProgressRing(
    progress: 0.62,
    outerStyle: RingAppearance(
        tint: .systemGray,
        thickness: 28
    ),
    innerStyle: RingAppearance(
        tint: .systemIndigo,
        thickness: 14,
        spacing: 6
    )
)

Personnalisation visuelle approfondie

Chaque anneau est configuré via une structure RingAppearance, qui encapsule :

  • tint : couleur appliquée (supporte les systèmes de couleurs dynamiques)
  • thickness : épaisseur du trait, en points logiques
  • spacing : espace entre les deux anneaux
  • cap : forme des extrémités du trait (.round, .square)
  • dashed : tableau de segments pour un tracé en pointillés

Exemple de style ponctué :

let dashedRing = RingAppearance(
    tint: .systemTeal,
    thickness: 18,
    cap: .round,
    dashed: [4, 6]
)

Mode indéterminé et animation continue

Pour les états de chargement sans progressino mesurable, utilisez IndeterminateRing. Ce composant anime automatiquement une rotation continue avec une trajectoire elliptique subtile :

IndeterminateRing(
    speed: 0.8,
    lineWidth: 22
)

Le paramètre speed ajuste la vitesse relative de l’animation (valeur par défaut : 1.0).

Intégration avec les animations SwiftUI

Les transitions de progression sont gérées nativement via les modificateurs animation(_:value:) ou withAnimation. Pour une interpolation fluide :

@State private var value: Double = 0.0

var body: some View {
    ProgressRing(progress: value, ...)
        .animation(.easeOut(duration: 0.7), value: value)
    
    Button("Avancer") {
        value = min(1.0, value + 0.25)
    }
}

Adaptation au contexte d’utilisation

Le composant s’adapte à divers scénarios grâce à des extensions spécialisées :

  • Téléchargement : affichage combiné avec un texte dynamique (ex. "37 Mo / 92 Mo")
  • Objectif quotidien : combinaison avec un badge de statut (ex. « ✅ Atteint »)
  • Minuteur : liaison à un Timer ou à une Publisher Combine pour mise à jour en temps réel

Contrôle des dimensions et du positionnement

La taille du cercle est contrôlée via le modificateur .frame(). Contrairement aux vues basées sur des contraintes fixes, cette approche permet un redimensionnement réactif selon les traits d’appareil ou les orientations :

ProgressRing(progress: 0.45)
    .frame(width: 180, height: 180)
    .padding(.horizontal, 16)

Personnalisation du texte central

Le texte affiché au centre peut être entièrement remplacé. L’API accepte une fermeture retournant un some View, ce qui autorise des compositions complexes :

ProgressRing(
    progress: 0.77,
    centerContent: { 
        VStack(spacing: 4) {
            Text("\(Int(0.77 * 100))%")
                .font(.headline)
                .fontWeight(.semibold)
            Text("Terminé")
                .font(.caption)
                .foregroundColor(.secondary)
        }
    }
)

Étiquettes: Swift SwiftUI iOS progress-ring ui-component

Publié le 14 octobre à 04h45