SCours SwiftUI
Fiche 03.09

Fiche 03.09 — Listes avancées : sections, swipe, suppression, édition

Objectif

Comprendre comment créer des listes SwiftUI plus complètes avec List, Section, ForEach, swipeActions, .onDelete, .onMove et EditButton.

Les listes sont utilisées partout : paramètres, messages, notifications, produits, favoris, historiques, etc.

1. Liste simple avec modèle Identifiable

Swift
import SwiftUI struct TaskItem: Identifiable { let id = UUID() var title: String var isDone: Bool } struct TaskListView: View { @State private var tasks = [ TaskItem(title: "Lire la documentation SwiftUI", isDone: false), TaskItem(title: "Créer un composant bouton", isDone: true), TaskItem(title: "Tester la navigation", isDone: false) ] var body: some View { List(tasks) { task in HStack { Image(systemName: task.isDone ? "checkmark.circle.fill" : "circle") Text(task.title) } } .navigationTitle("Tâches") } }

List fonctionne très bien avec des modèles qui respectent Identifiable.

2. Liste avec Section

Swift
struct SettingsListView: View { var body: some View { List { Section("Compte") { SettingsRow(title: "Profil", icon: "person") SettingsRow(title: "Sécurité", icon: "lock") } Section("Application") { SettingsRow(title: "Notifications", icon: "bell") SettingsRow(title: "Apparence", icon: "paintbrush") } } .navigationTitle("Réglages") } } struct SettingsRow: View { let title: String let icon: String var body: some View { Label(title, systemImage: icon) } }

Section permet de regrouper visuellement les éléments.

3. Cellule custom

Swift
struct UserRowView: View { let name: String let subtitle: String let imageName: String var body: some View { HStack(spacing: AppSpacing.medium) { Image(systemName: imageName) .font(.title2) .frame(width: 40, height: 40) .background(AppColors.cardBackground) .clipShape(Circle()) VStack(alignment: .leading, spacing: AppSpacing.extraSmall) { Text(name) .font(AppTypography.body.weight(.semibold)) Text(subtitle) .font(AppTypography.caption) .foregroundStyle(AppColors.textSecondary) } Spacer() } .padding(.vertical, AppSpacing.small) } }

Utilisation :

Swift
List { UserRowView( name: "Guillaume", subtitle: "Développeur iOS", imageName: "person.fill" ) }

4. Swipe actions

swipeActions permet d’ajouter des actions quand l’utilisateur glisse une cellule.

Swift
struct SwipeTaskListView: View { @State private var tasks = [ TaskItem(title: "Préparer le design system", isDone: false), TaskItem(title: "Créer le formulaire", isDone: false) ] var body: some View { List { ForEach(tasks) { task in Text(task.title) .swipeActions(edge: .trailing) { Button(role: .destructive) { delete(task) } label: { Label("Supprimer", systemImage: "trash") } } .swipeActions(edge: .leading) { Button { toggleDone(task) } label: { Label("Terminé", systemImage: "checkmark") } .tint(.green) } } } } private func delete(_ task: TaskItem) { tasks.removeAll { $0.id == task.id } } private func toggleDone(_ task: TaskItem) { guard let index = tasks.firstIndex(where: { $0.id == task.id }) else { return } tasks[index].isDone.toggle() } }

Les swipe actions sont utiles pour supprimer, archiver, marquer comme lu, ajouter aux favoris, etc.

5. Suppression avec onDelete

onDelete fonctionne avec ForEach.

Swift
struct DeleteTaskListView: View { @State private var tasks = [ TaskItem(title: "Créer LoginView", isDone: false), TaskItem(title: "Créer RegisterView", isDone: false), TaskItem(title: "Ajouter validation", isDone: false) ] var body: some View { List { ForEach(tasks) { task in Text(task.title) } .onDelete { offsets in tasks.remove(atOffsets: offsets) } } .navigationTitle("Tâches") } }

offsets correspond aux positions des éléments supprimés dans la liste.

6. Déplacement avec onMove

Swift
struct MoveTaskListView: View { @State private var tasks = [ TaskItem(title: "Accueil", isDone: false), TaskItem(title: "Recherche", isDone: false), TaskItem(title: "Profil", isDone: false) ] var body: some View { List { ForEach(tasks) { task in Text(task.title) } .onMove { source, destination in tasks.move(fromOffsets: source, toOffset: destination) } } .navigationTitle("Ordre des onglets") .toolbar { EditButton() } } }

EditButton active automatiquement le mode édition de la liste.

7. Suppression et édition dans la même liste

Swift
struct EditableTaskListView: View { @State private var tasks = [ TaskItem(title: "Écran Home", isDone: false), TaskItem(title: "Écran Profil", isDone: false), TaskItem(title: "Écran Settings", isDone: false) ] var body: some View { List { ForEach(tasks) { task in HStack { Text(task.title) Spacer() if task.isDone { Image(systemName: "checkmark.circle.fill") .foregroundStyle(.green) } } } .onDelete { offsets in tasks.remove(atOffsets: offsets) } .onMove { source, destination in tasks.move(fromOffsets: source, toOffset: destination) } } .navigationTitle("Projet") .toolbar { EditButton() } } }

8. Navigation liste vers détail

Swift
struct Animal: Identifiable { let id = UUID() let name: String let type: String } struct AnimalListView: View { let animals = [ Animal(name: "Milo", type: "Chat"), Animal(name: "Rex", type: "Chien") ] var body: some View { NavigationStack { List(animals) { animal in NavigationLink { AnimalDetailView(animal: animal) } label: { VStack(alignment: .leading) { Text(animal.name) .font(.headline) Text(animal.type) .font(.caption) .foregroundStyle(.secondary) } } } .navigationTitle("Animaux") } } } struct AnimalDetailView: View { let animal: Animal var body: some View { VStack(spacing: AppSpacing.medium) { Text(animal.name) .font(AppTypography.title) Text(animal.type) .foregroundStyle(AppColors.textSecondary) } .navigationTitle(animal.name) } }

NavigationLink dans une List est très courant pour aller vers un écran détail.

9. Limites de List

List est très pratique, mais son style est parfois difficile à personnaliser complètement.

Pour une interface très custom, tu peux préférer :

Swift
ScrollView { LazyVStack(spacing: AppSpacing.medium) { ForEach(items) { item in CustomCard(item: item) } } .padding() }

List est idéale pour les listes iOS classiques.

ScrollView + LazyVStack est mieux pour les feeds, cards custom, interfaces très visuelles ou designs plus libres.

Points à connaître

onDelete et onMove doivent être attachés à un ForEach, pas directement à List.

Pour modifier un élément, il faut retrouver son index dans le tableau.

EditButton fonctionne très bien avec les listes éditables.

Pour des listes très personnalisées, LazyVStack donne souvent plus de contrôle que List.

Résumé

  • List sert aux listes iOS classiques.
  • Section permet de regrouper les contenus.
  • swipeActions ajoute des actions latérales.
  • .onDelete permet la suppression.
  • .onMove permet le réordonnancement.
  • EditButton active le mode édition.
  • Pour un design très custom, préfère parfois ScrollView + LazyVStack.