Introduction aux vues dynamiques de la collection dans SwiftUI

Les applications iOS modernes exigent des interfaces fluides et adaptatives qui répondent gracieusement aux différentes tailles de données, orientations des appareils et dimensions d'écran. Dans UIKit, les vues de collection fournissent un mécanisme puissant mais verbeux pour construire des mises en page de type grille. SwiftUI simplifie considérablement cette fonctionnalité avec son API déclarative, notamment par LazyVGrid[ et LazyHGrid[.Ces composants permettent aux développeurs de définir des grilles dynamiques et résilientes avec un code minimal, ce qui facilite la construction d'interfaces de production prêtes à fonctionner sur les iPhones, les iPads et même les cibles de Mac Catalyst.

Une mise en page dynamique de la collection[ adapte son comportement de calcul de colonne, de hauteur de ligne, d'espacement ou de dimensionnement en fonction de la largeur du conteneur, du nombre d'éléments ou du contenu lui-même. Cet article vous guidera dans la construction de telles mises en page dans SwiftUI, couvrant tout ce qui va de la configuration de base de la grille aux comportements adaptatifs avancés, à l'accord de performance et à l'intégration avec les animations.

Composantes principales : LazyVGrid et LazyHGrid

SwiftUI fournit deux conteneurs de grille primaire : LazyVGrid (grille de défilement vertical) et LazyHGrid (grille de défilement horizontal).Les deux chargent paresseusement leurs enfants, ce qui signifie que les vues ne sont créées que lorsqu'elles deviennent visibles à l'écran – une caractéristique cruciale pour la manipulation de grands ensembles de données sans ballonnement de mémoire.

Configuration de l'élément de grille

Pour définir une structure de grille, vous créez un tableau d'instances GridItem. Chaque représente une seule colonne (dans ) ou une seule ligne (dans ). Vous contrôlez trois propriétés clés :

  • Taille – Un des trois modes de calibrage : , ou .
  • Space – Espacement horizontal entre les éléments d'une même rangée (pour les grilles verticales) ou espacement vertical (pour les grilles horizontales).
  • Alignement – Contrôle la façon dont les éléments s'alignent dans une cellule de grille. Rarement modifié par défaut.

Le choix du mode de calibrage définit la flexibilité générale de la disposition :

  • Fixed – Largeur explicite (ou hauteur) pour chaque colonne/ligne. Utile lorsque vous avez besoin d'un calibrage précis, p.ex. une disposition de trois colonnes de cellules fixes de 100 points.
  • Flexible – Chaque élément de grille peut s'étirer pour remplir l'espace disponible, avec des limites minimales et maximales en option. C'est le choix le plus courant pour les colonnes dynamiques et à espacement uniforme.
  • Adaptive – La grille détermine automatiquement le nombre d'éléments par ligne de colonne en fonction d'une taille minimale spécifiée. C'est la façon la plus simple de faire une mise en page dynamique qui répond à la largeur de l'écran.

Par exemple, pour créer une grille qui correspond automatiquement au plus grand nombre de colonnes possible, chacune d'une largeur minimale de 100 points, vous écrivez :

let columns = [
 GridItem(.adaptive(minimum: 100))
]

Cette ligne unique donne une mise en page qui s'adapte d'une colonne sur un iPhone étroit à plusieurs colonnes sur un iPad dans le paysage – exactement le comportement que nous voulons pour les mises en page dynamiques.

Construire une grille dynamique de base

Let="s commence par une grille simple et entièrement dynamique en utilisant le dimensionnement . Ci-dessous est une vue complète qui affiche un tableau de chaînes dans une grille réactive:

struct AdaptiveGridView: View {
 let items = Array(1...20).map { "Item \($0)" }

 var body: some View {
 ScrollView {
 LazyVGrid(
 columns: [GridItem(.adaptive(minimum: 80), spacing: 12)],
 spacing: 12
 ) {
 ForEach(items, id: \.self) { item in
 Text(item)
 .frame(minWidth: 80, maxWidth: .infinity, minHeight: 80)
 .background(Color.blue.opacity(0.2))
 .cornerRadius(8)
 }
 }
 .padding()
 }
 }
}

Ici, chaque article est forcé à au moins 80 points de largeur et de hauteur. L'élément de grille calcule automatiquement le nombre d'articles pouvant s'adapter par rangée compte tenu de la largeur disponible, en respectant la largeur minimale. Le résultat est une disposition qui se retransmet sans heurt lorsque l'appareil tourne ou la fenêtre est redimensionnée (sur iPad ou Mac).

Cette approche fonctionne bien pour le contenu simple, mais pour plus de contrôle, vous devez souvent combiner avec des éléments, ou utiliser pour calculer le nombre de colonnes personnalisées.

Utilisation de GeometryReader pour des comptes de colonnes dynamiques précis

Alors que gère de nombreux cas, parfois vous voulez définir un nombre exact de colonnes en fonction de la largeur du conteneur – par exemple, toujours 2 colonnes sur un iPhone en portrait, 3 dans le paysage et 4 sur iPad. La vue GeometryReader fournit la taille actuelle de son parent, vous permettant de calculer dynamiquement le nombre optimal de colonnes.

Exemple : Un dénombrement de colonnes adaptatives

La vue suivante lit la largeur disponible et calcule un nombre de colonnes en fonction d'une largeur cible :

struct DynamicColumnGridView: View {
 let items = Array(1...30).map { "Item \($0)" }
 let targetItemWidth: CGFloat = 120

 var columns: [GridItem] {
 let count = max(Int(geometryProxy.size.width / targetItemWidth), 1)
 return Array(repeating: GridItem(.flexible(), spacing: 12), count: count)
 }

 var body: some View {
 GeometryReader { geometry in
 ScrollView {
 let width = geometry.size.width
 let count = max(Int(width / targetItemWidth), 1)
 let columns = Array(repeating: GridItem(.flexible(), spacing: 12), count: count)

 LazyVGrid(columns: columns, spacing: 12) {
 ForEach(items, id: \.self) { item in
 Text(item)
 .frame(minWidth: 0, maxWidth: .infinity, minHeight: 80)
 .background(Color.green.opacity(0.3))
 .cornerRadius(8)
 }
 }
 .padding()
 }
 }
 }
}

Important: Dans la pratique, vous devez stocker la valeur à l'intérieur d'une variable ou l'utiliser dans la même fermeture. L'exemple ci-dessus utilise une variable d'aide à l'intérieur de la fermeture pour la clarté. Assurez-vous toujours de ne pas envelopper inutilement tout le corps dans – il peut briser la disposition si utilisé comme la vue la plus haut.

Cette technique vous donne un contrôle total sur le nombre de colonnes tout en gardant les éléments flexibles afin qu'ils remplissent la largeur uniformément. Vous pouvez également ajouter des contraintes minimales et maximales aux éléments flexibles pour éviter les cellules extrêmement tendues ou écrasées.

Adapter dynamiquement les hauteurs de lignes

Les mises en page dynamiques ne sont pas seulement sur les colonnes – les hauteurs de rangée peuvent également varier en fonction du contenu. SwiftUI ne fournit pas une grille directe -size automatique comme UIKit-S avec des tailles estimées. Cependant, vous pouvez obtenir des résultats similaires en utilisant la taille intrinsèque du contenu et ] ou en intégrant des hauteurs entraînées par des données.

Par exemple, vous pouvez faire chaque cellule de grille de taille auto en omettant des hauteurs explicites et en laissant le contenu interne déterminer la taille. SwiftUI calculera automatiquement la hauteur de cellule en fonction de l'élément le plus haut de chaque rangée (pour les grilles verticales). Mais si vous avez besoin de hauteurs de rangée vraiment variables (comme une disposition maçonnière), vous avez besoin d'une approche personnalisée – souvent en utilisant avec des guides d'alignement personnalisés ou enveloppant la grille dans un mais avec différentes hauteurs par colonne. Cela est plus avancé et au-delà de la portée de cet article, mais la clé à retenir est que suppose que toutes les cellules d'une rangée partagent la même hauteur (déterminée par la cellule la plus haute).

Manipulation des ensembles de données volumineux avec chargement paresseux

L'un des principaux avantages de et est leur comportement de chargement paresseux. Ils ne créent que des vues pour les éléments actuellement visibles (plus un petit tampon). Cela les rend efficaces pour les ensembles de données avec des centaines ou même des milliers d'éléments. Cependant, il y a quelques bonnes pratiques pour maintenir une performance lisse:

  • Utiliser des identifiants stables – Toujours fournir un paramètre unique à . Pour les modèles de données de structure qui sont conformes à , SwiftUI peut diffuser efficacement.
  • Éviter les calculs coûteux à l'intérieur du corps – Gardez les vues cellulaires simples. Si vous avez besoin de calculs complexes de mise en page, précalculez-les en dehors de la grille.
  • Leverage – Si votre vue cellulaire est conforme à ou utilise le modificateur , SwiftUI peut sauter le rendu des changements lorsque les données de la cellule n'ont pas changé.
  • ]Utiliser à l'intérieur d'un – Si vous nichez un à l'intérieur d'un autre contenant paresseux, vous pouvez briser la charge paresseuse.

Combiner les mises en page dynamiques et les animations

Les animations en SwiftUI peuvent rendre les changements de disposition naturels et polis. Par exemple, lorsque le nombre de colonnes change (en raison de la rotation ou de la redimensionnement de la fenêtre), vous pouvez animer les éléments de la grille vers leurs nouvelles positions. La manière la plus simple est d'envelopper la grille dans un modificateur ou d'utiliser le modificateur avec une valeur qui déclenche le changement.

Changements de colonnes d'animation

Voici une extension de l'exemple de colonne dynamique antérieure avec une transition animée:

struct AnimatedDynamicGridView: View {
 let items = Array(1...20).map { "Item \($0)" }
 @State private var targetItemWidth: CGFloat = 120

 var body: some View {
 GeometryReader { geometry in
 ScrollView {
 let width = geometry.size.width
 let count = max(Int(width / targetItemWidth), 1)
 let columns = Array(repeating: GridItem(.flexible(), spacing: 12), count: count)

 LazyVGrid(columns: columns, spacing: 12) {
 ForEach(items, id: \.self) { item in
 Text(item)
 .frame(minWidth: 0, maxWidth: .infinity, minHeight: 80)
 .background(Color.orange.opacity(0.4))
 .cornerRadius(8)
 .transition(.scale.combined(with: .opacity))
 }
 }
 .padding()
 .animation(.spring(response: 0.4, dampingFraction: 0.7), value: count)
 }
 }
 }
}

En joignant l'animation à la valeur , SwiftUI animera l'insertion, la suppression et la réorganisation des éléments lorsque le nombre de colonnes change. Vous pouvez également utiliser pour des transitions plus avancées entre les mises en page.

Exemples pratiques : Layouts dynamiques du monde réel

Laissez-vous guider par deux scénarios communs qui bénéficient de la mise en page dynamique de la collection.

Galerie de photos Grille

Une galerie de photos typique utilise une grille de cellules carrées qui s'adapte à la largeur de l'écran. Vous voulez que les cellules soient aussi grandes que possible tout en conservant un nombre fixe de colonnes (par exemple, 2 sur iPhone, 3 sur iPhone Plus, 4 sur iPad).

struct PhotoGalleryGrid: View {
 let images: [String]

 @Environment(\.horizontalSizeClass) var sizeClass

 var columns: [GridItem] {
 let count = sizeClass == .compact ? 2 : 4
 return Array(repeating: GridItem(.flexible(), spacing: 2), count: count)
 }

 var body: some View {
 ScrollView {
 LazyVGrid(columns: columns, spacing: 2) {
 ForEach(images, id: \.self) { imageName in
 Image(imageName)
 .resizable()
 .aspectRatio(1, contentMode: .fill)
 .clipped()
 }
 }
 .padding(.horizontal, 2)
 }
 }
}

Tableau de bord dynamique avec tailles de cellules variées

Pour un tableau de bord, vous pouvez avoir des cellules de différentes largeurs – par exemple, un grand graphique prenant deux colonnes, des statistiques plus petites prenant une colonne chacune. Vous pouvez y parvenir en combinant des éléments avec des multiplicateurs explicites en utilisant un tableau personnalisé . Par exemple:

let columns: [GridItem] = [
 GridItem(.flexible(minimum: 80), spacing: 12), // small stat cell
 GridItem(.flexible(minimum: 160), spacing: 12), // wide cell
 GridItem(.flexible(minimum: 80), spacing: 12) // small stat cell
]

Ensuite, dans les grilles , vous pouvez cartographier différents types de données à différentes échelles de colonnes en enveloppant des éléments dans un conteneur qui s'étend pour remplir la largeur de grille. Cela nécessite une coordination soigneuse car ne supporte pas nativement les échelles multicolonnes. Un correctif commun est d'utiliser modificateur (disponible dans iOS 16+) ou indexer manuellement la grille en divisant vos données en sections que chacune remplit une ligne complète.

Pour iOS 16 et plus tard, vous pouvez utiliser le modificateur sur une vue à l'intérieur de la grille pour spécifier le nombre de colonnes que cette vue devrait couvrir:

LazyVGrid(columns: columns, spacing: 12) {
 ForEach(dashboardItems, id: \.id) { item in
 DashboardCell(item: item)
 .gridCellColumns(item.columnSpan)
 }
}

C'est la façon la plus propre de créer un véritable tableau de bord dynamique avec des tailles de cellules mixtes.

Considérations de performance pour les ensembles de données volumineux

Lorsque vous traitez des milliers d'articles, même un chargement paresseux peut entraîner des décalages si chaque cellule est complexe. Voici des techniques avancées pour garder vos grilles dynamiques lisses:

  • Précédentes images et données[ – Utiliser ou pour charger les données de façon asynchrone, et non dans le corps de la cellule.
  • ] pour les listes extrêmement grandes – SwiftUI=s est plus optimisé pour des milliers de lignes avec dimensionnement dynamique. Cependant, pour une mise en page de grille, avec une mise en page personnalisée est possible mais plus difficile à réaliser; envisager d'utiliser via ] si la performance devient un vrai goulot d'étranglement.
  • Limiter la recomputation de la mise en page – Évitez de mettre la logique de mise en page dynamique dans le corps de la cellule.
  • Utiliser sur toute la grille – Si vous changez complètement la structure de la grille (p. ex., passer de 2 colonnes à 3), en attribuant une nouvelle à la grille l'obligeant à recharger entièrement, ce qui peut être plus rapide que d'essayer d'animer un changement complexe.

Ressources externes pour approfondir vos connaissances

Pour explorer plus en détail les points de vue dynamiques sur la collection dans SwiftUI, veuillez consulter ces ressources officielles et communautaires :

  1. Apple="LazyVGrid Documentation – La référence officielle pour tous les paramètres et comportements. Apple Developer Documentation: LazyVGrid
  2. Guide de démarrage rapide pour GridItem – Couvre les trois stratégies de dimensionnement avec des exemples interactifs.
  3. SwiftUI par exemple – Mises en page de grille – Un tutoriel pratique de Hacking avec Swift qui couvre les grilles adaptatives et flexibles. Hacking avec Swift: Mises en page de grille
  4. SwiftUI Lab: Grilles lassées – Plongez profondément dans les performances et les personnalisations. SwiftUI Lab: Grilles lassées

Résumé des pratiques exemplaires

La création d'une présentation dynamique de la vue de collection dans SwiftUI nécessite une flexibilité d'équilibrage avec les performances. Voici une liste de contrôle rapide pour les grilles prêtes à la production :

  • Commencez par pour le nombre de colonnes dynamiques le plus simple; utilisez seulement lorsque vous avez besoin d'un contrôle précis.
  • Fournir toujours des valeurs uniques ou conformes à .
  • Utilisez avec une valeur qui change (comme le nombre de colonnes) pour lisser les transitions.
  • Pour les tailles de cellules mixtes, préférez sur iOS 16+ ou concevoir une mise en page personnalisée.
  • Profilez votre grille avec Instruments pour vérifier que le chargement paresseux fonctionne et que les cellules ne rendent pas le contenu hors écran.
  • Tester sur plusieurs tailles et orientations d'appareils – des mises en page dynamiques qui ont l'air superbes sur un écran peuvent se briser sur un autre.

La maîtrise des vues de collection dynamique vous permet de construire des applications qui se sentent natives et réactives sur chaque appareil Apple. Avec les outils de déclaration SwiftUI , vous pouvez atteindre ce qui une fois requis des dizaines de lignes de code UIKit dans une poignée de composants expressifs et réutilisables.