Avec SwiftUI, la vue List permet d'afficher des informations l'une sous l'autre et, selon les option que vous mettez en place, d'y effectuer une sélection.
Elle assure que l'écran pourra défiler si le contenu déborde en hauteur.
Bien que ce ne soit pas obligatoire, la liste est plus facile à créer à partir d'un tableau dont les éléments répondent au protocole Identifiable.
import SwiftUI
struct Item: Identifiable {
var id: Int
var code: String
var titre: String
var couleur: Color
}
Au besoin, la structure pourra comprendre un identifiant autogénéré à l'aide de UUID().
struct Item: Identifiable {
var id: UUID = UUID()
var code: String
var titre: String
var couleur: Color
}
Il est désormais possible d'utiliser un tableau de Item dans une liste :
struct ContentView: View {
private var items: [Item] = [
Item(code: "abc", titre: "Item 1", couleur: .blue),
Item(code: "def", titre: "Item 2", couleur: .red),
Item(code: "ghi", titre: "Item 3", couleur: .green),
]
var body: some View {
List(items) {
Text($0.titre)
}
}
}
Si vous préférez travailler avec un nom de variable plutôt qu'avec $0 :
List(items) { item in
Text(item.titre)
}

On voit que par défaut, le fond de l'écran, derrière la liste, apparaît en gris. Cette apparence peut être changée à l'aide du modifieur scrollContentBackground().
List(items) { item in
Text(item.titre)
}
.scrollContentBackground(.hidden)

Dans le cas où les éléments de la liste ne répondent pas au protocole Identifiable, par exemple une liste de String, il faudra spécifier qu'est-ce qui identifie chaque élément de façon unique.
Dans le cas le plus simple, on identifiera chaque élément à l'aide de .self. Ceci fonctionnera à condition que l'élément réponde au protocole Hashable. C'est le cas, par exemple, pour les entiers et les chaînes.
struct ContentView: View {
private var donnees: [String] = ["a", "b", "c", "d", "e", "f", "g"]
var body: some View {
List(donnees, id: \.self) { donnee in
Text(donnee)
}
}
}
Dans le cas d'une structure, si chaque propriété est un entier ou une chaîne, il faudra simplement ajouter : Hashable à la déclaration de la structure.
Voici un exemple avec un tableau d'items :
// la structure se conforme au protocole Hashable sans rien lui ajouter de plus puisque toutes ses propriétés
// se conforment également au protocole Hashable
struct Jeu: Hashable {
var nom: String
var niveau: Int
}
struct ContentView: View {
private var jeux: [Jeu] = [
Jeu(nom: "Super Mario Bros.", niveau: 2),
Jeu(nom: "The Legend of Zelda", niveau: 3),
Jeu(nom: "Pac-Man", niveau: 2),
Jeu(nom: "Tetris", niveau: 1)
]
var body: some View {
List(jeux, id: \.self) { jeu in
Text(jeu.nom)
}
}
}
Dans des cas plus complexes, vous pouvez indiquer clairement quelle propriété permettra d'identifier l'élément de façon unique.
Par exemple, si on a une structure Etudiant qui contient une propriété code unique :
struct Etudiant {
var code: String // le code devra être unique dans le tableau d'étudiants
var prenom: String
var nom: String
}
struct ContentView: View {
private var etudiants: [Etudiant] = [...]
var body: some View {
List(etudiants, id: \.code) { etudiant in
Text(etudiant.prenom)
}
}
}
Il est possible d'afficher l'information sous la forme qui vous convient.
List(items) { item in
HStack {
Image(systemName: "paperclip")
Text(item.code)
.foregroundColor(item.couleur)
Text("-")
Text(item.titre)
.foregroundColor(item.couleur)
}
}

ForEach est une structure qui permet de générer des vues à partir d'une boucle.
Elle est beaucoup moins puissante que List. Cependant, la combinaison des deux est très intéressante puisqu'elle nous donne plus de flexibilité pour la mise en forme.
Voici un exemple de base :
List {
ForEach(items) { item in
Text(item.titre)
}
}

Voici un exemple qui permet d'afficher des sous-catégories :
List {
Section(header: Label("Catégorie 1", systemImage: "seal")) {
ForEach(itemsCategorie1) { item in
HStack {
Text(item.code)
.foregroundColor(item.couleur)
Text("-")
Text(item.titre)
.foregroundColor(item.couleur)
}
}
}
Section(header: Label("Catégorie 2", systemImage: "cone")) {
ForEach(itemsCategorie2) { item in
HStack {
Text(item.code)
.foregroundColor(item.couleur)
Text("-")
Text(item.titre)
.foregroundColor(item.couleur)
}
}
}
}

Avec onTapGesture, il est possible de déterminer quel élément est sélectionné.
Remarquez que onTapGesture doit être placé à l'intérieur du List. On ne réagit pas à un doigt sur la liste mais bien à un doigt sur un élément de la liste.
struct ContentView: View {
private var items: [Item] = [
Item(code: "abc", titre: "Item 1", couleur: .blue),
Item(code: "def", titre: "Item 2", couleur: .red),
Item(code: "ghi", titre: "Item 3", couleur: .green),
]
@State private var selection: Item?
var body: some View {
VStack {
List(items) { item in
Text(item.titre)
.onTapGesture {
selection = item
}
}
if selection != nil {
Text(selection!.titre)
}
else {
Text("Aucune sélection")
}
}
}
}
Attention : si la liste contient des NavigationLink, il faudra plutôt utiliser la technique présentée sur la fiche « Action au toucher d'un NavigationLink en plus du changement de vue ».
Pour permettre la sélection multiple, il faudra travailler avec un tableau pour retenir les éléments sélectionnés.
struct ContentView: View {
private var items: [Item] = [
Item(code: "abc", titre: "Item 1", couleur: .blue),
Item(code: "def", titre: "Item 2", couleur: .red),
Item(code: "ghi", titre: "Item 3", couleur: .green),
]
@State private var selections: [Item] = []
var body: some View {
VStack {
List(items){ item in
Text(item.titre)
.onTapGesture {
if selections.firstIndex(where: { $0.id == item.id }) == nil {
selections.append(item)
} else {
selections.removeAll(where: { $0.id == item.id })
}
}
}
if selections.count > 0 {
List(selections) { item in
Text(item.titre)
}
}
else {
Text("Aucune sélection")
}
}
}
}
Pour que l'item sélectionné soit différent visuellement, il est possible d'utiliser listRowBackground.
Notez que vous devrez utiliser ForEach pour que ceci fonctionne.
De plus, dans que cet extrait de code, pour effectuer une égalité entre l'item sélectionné et l'item affiché, la structure devra répondre au protocole Equatable.
import Foundation
import SwiftUI
struct Item: Identifiable, Equatable {
var id: UUID = UUID()
var code: String
var titre: String
var couleur: Color
}
List {
ForEach(items) { item in
HStack {
Image(systemName: "paperclip")
Text(item.code)
.foregroundColor(item.couleur)
Text("-")
Text(item.titre)
.foregroundColor(item.couleur)
}
.listRowBackground(selection == item ?
Rectangle()
.foregroundColor(.gray)
.opacity(0.1)
: nil
)
.onTapGesture {
selection = item
}
}
}

« SwiftUI - Dynamic List & Identifiable ». Medium. https://medium.com/flawless-app-stories/swiftui-dynamic-list-identifiable-73c56215f9ff
« What is the difference between List and ForEach in SwiftUI? ». Stack Overflow. https://stackoverflow.com/questions/56535326/what-is-the-difference-between-list-and-foreach-in-swiftui
« Why does \.self work for ForEach? ». Hacking with Swift. https://www.hackingwithswift.com/books/ios-swiftui/why-does-self-work-for-foreach
« How to conform to the Hashable protocol ». Hacking with Swift. https://www.hackingwithswift.com/example-code/language/how-to-conform-to-the-hashable-protocol
▼Publicité