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

65.6 Demander une confirmation avec .confirmationDialog()


La méthode confirmationDialog() permet d'afficher une boîte de confirmation sous forme de popup, pour permettre à l'usager d'accepter ou de refuser une proposition.

Dans cette fiche :

confirmationDialog vs alert

Le fonctionnement de confirmationDialog ressemble à celui de alert : une variable d'état contrôle si le popup doit être affiché ou non et on peut définir quels boutons seront affichés et quelles actions ils déclencheront.

Voici comment choisir entre une alerte et une boîte de confirmation :

  • L'alerte, qui apparaît au centre de l'écran, sera généralement utilisée pour afficher un message à l'usager ou lui demander de prendre action.
  • La boîte de confirmation, quant à elle, apparaît normalement vis-à-vis la vue qui a déclenché son affichage. Elle est utilisée pour demander à l'usager de confirmer une action qu'il a réalisée, par exemple un clic sur une icône de suppression.

Fonctionnement du confirmationDialog

La boîte de confirmation est constituée d'un message suivi d'au moins un bouton, qui permet de réaliser l'action.

Depuis iOS 26, il n'y a plus de bouton d'annulation par défaut. De plus, si un bouton a le rôle .cancel, il ne sera pas affiché. Un bouton .cancel est requis seulement lorsqu'il y a des actions à réaliser lorsque l'usager clique en dehors de la boîte de confirmation.

Je vous propose une technique qui permet d'afficher un bouton d'annulation. Cette technique permet de gérer à la fois le clic sur le bouton d’annulation et la fermeture du dialogue lorsqu’on clique à l’extérieur.

Remarquez l'utilisation d'une vue enfant qui définit chaque élément de la liste. Ceci est essentiel pour que chaque ligne possède son propre état, ce qui permet à SwiftUI d'afficher le popup vis-à-vis la ligne sur laquelle l'usager a cliqué.

Remarquez également comment la vue enfant réussit à modifier la liste, qui est définie dans la vue parent. La vue enfant ne modifie pas directement la liste. Elle reçoit en paramètre une fonction qui permet de modifier la liste.

Fichier ContentView.swift

struct ContentView: View {

  @State private var items: [Item] = [
    Item(id: 1, code: "ST-001", titre: "Crayon HB"),
    Item(id: 2, code: "RL-010", titre: "Règle 30cm"),
    Item(id: 3, code: "MS-202", titre: "Souris sans fil"),
    Item(id: 4, code: "KB-105", titre: "Clavier mécanique"),
    Item(id: 5, code: "NT-330", titre: "Carnet de notes"),
    Item(id: 6, code: "ER-005", titre: "Gomme à effacer"),
    Item(id: 7, code: "MK-772", titre: "Marqueur noir")
  ]

  var body: some View {
    NavigationStack {
      List(items) { item in
        // vue enfant obligatoire pour que le popup s'affiche au bon endroit
        UnItem(item: item) {
          items.removeAll { $0.id == item.id }   // ceci est le code de la fonction qui est passée en paramètre à la vue enfant
          ...   // autres opérations au besoin
        }
      }
      .toolbar(content: {
        ToolbarItem(placement: .principal, content: {
          Text("Fournitures scolaires")
        })
      })
    }
  }
}

Fichier UnItem.swift

struct UnItem: View {
  let item: Item
  let onDelete: () -> Void   // ce code est défini dans la vue parent

  @State private var afficherPopup: Bool = false

  var body: some View {
    HStack(spacing: 4) {
      Image(systemName: "tag")
        .padding(10)
        .frame(width: 70)
      VStack(alignment: .leading) {
        Text(item.code)
        Text(item.titre)
      }

      Spacer()

      Button(role: .destructive, action: {
        afficherPopup = true
      }) {
        Image(systemName: "trash")
          .padding(.trailing)
      }
      .buttonStyle(.borderless)
      .confirmationDialog("Supprimer l'item \(item.titre)?", isPresented: $afficherPopup, titleVisibility: .visible) {
        Button("Supprimer", role: .destructive) {
          onDelete()
          print("Item \(item.code) supprimé.")
        }

        // depuis iOS 26 : on ajoute un bouton d'annulation qui n'a pas le rôle .cancel
        Button("Annuler") {
          print("Bouton Annuler cliqué")
          nettoyage()
        }

        // depuis iOS 26 : ce bouton ne sera jamais affiché mais il permet de dire quoi faire quand on clique en dehors du popup
        Button("", role: .cancel) {
          print("Clic en dehors du popup")
          nettoyage()
        }
      }
    } // fin HStack
  }

  func nettoyage() {
    print("Opération nettoyage")
  }
}

confirmationDialog()

Et si le bouton qui appelle le confirmationDialog n'est pas dans une liste?

Le bouton qui permet d'afficher le confirmationDialog pourrait être placé en dehors d'une liste, par exemple dans le bas d'une vue de détail.

Puisque la vue de détail est une vue enfant, elle a déjà son propre état. Elle peut donc contenir le confirmationDialog ainsi que tout le code qui l'entoure.

Dans le cas où la vue de détail doit modifier une liste utilisée ailleurs dans l'application, elle recevra en paramètre une fonction qui permet de modifier cette liste.

Si le confirmationDialog mène à une suppression, une fois la suppression réalisée, il faudra penser à utiliser dismiss() pour refermer la vue de détail.

Remarquez que le confirmationDialog a été attaché directement au bouton pour qu'il apparaisse vis-à-vis ce dernier.

Fichier DetailsItem.swift

struct DetailsItem: View {
  let item: Item
  let onDelete: () -> Void   // ce code est défini dans la vue parent

  @State private var afficherPopup: Bool = false
  ...

  var body: some View {
    VStack {
      ...
      Button(role: .destructive, action: {
        afficherPopup = true
      }) {
        Label("Supprimer l'item", systemImage: "trash")
        ...
      }
      .confirmationDialog("Supprimer l'item \(item.titre)?", isPresented: $afficherPopup, titleVisibility: .visible) {
        Button("Supprimer", role: .destructive) {
          onDelete()
          dismiss()
        }
        Button("Annuler") { nettoyage() }
        Button("", role: .cancel) { nettoyage() }
      }
    }
    ...
  }
  ...
}

ConfirmationDialog dans vue de détail

▼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