Formation PUB900 : Développer une application pour iPhone avec SwiftUI, H-2024 Quelques vues SwiftUI

65.11 List


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.

Protocole Identifiable

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.

Swift

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().

Swift

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 :

SwiftUI

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 :

SwiftUI

List(items) { item in
  Text(item.titre)
}

List simple

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().

SwiftUI

List(items) { item in
  Text(item.titre)
}
.scrollContentBackground(.hidden)

scrollContentBackground

Si les éléments ne répondent pas au protocole Identifiable

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.

SwiftUI

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 :

SwiftUI

// 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
}

SwiftUI

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 :

SwiftUI

struct Etudiant {
  var code: String   // le code devra être unique dans le tableau d'étudiants
  var prenom: String
  var nom: String
}

SwiftUI

struct ContentView: View {
  private var etudiants: [Etudiant] = [...]

  var body: some View {
    List(etudiants, id: \.code) { etudiant in
      Text(etudiant.prenom)
    }
  }
}

Affichage complexe

Il est possible d'afficher l'information sous la forme qui vous convient.

SwiftUI

List(items) { item in
  HStack {
    Image(systemName: "paperclip")
    Text(item.code)
      .foregroundColor(item.couleur)
    Text("-")
    Text(item.titre)
      .foregroundColor(item.couleur)
  }
}

List

Avec ForEach

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 :

SwiftUI

List {
  ForEach(items) { item in
    Text(item.titre)

  }
}

List

 

Voici un exemple qui permet d'afficher des sous-catégories :

Swift

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)
      }
    }
  }
}

List avec Foreach

Sélectionner un élément

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.

SwiftUI

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 ».

Sélectionner plusieurs éléments

Pour permettre la sélection multiple, il faudra travailler avec un tableau pour retenir les éléments sélectionnés.

SwiftUI

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")
      }
    }
  }
}

Changer l'apparence de l'item sélectionné

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.

Swift

import Foundation
import SwiftUI

struct Item: Identifiable, Equatable {
  var id: UUID = UUID()
  var code: String
  var titre: String
  var couleur: Color
}

SwiftUI

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
    }
  }
}

List avec élément sélecitonné visible

Pour plus d'information

« 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é

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre