Module 9 - Exercice

← Retour au module principal

Gestes et interactions tactiles

SwiftUI offre une approche déclarative pour détecter et réagir aux gestes utilisateurs. Grâce aux divers reconnaisseurs de gestes fournis par le framework, vous pouvez rendre vos vues interactives en implémentant des actions pour des taps, des appuis longs, des glissements, des pincements ou encore des rotations. Nous allons voir comment ajouter et composer ces gestes.

Création du projet GestureDemo

vant de commencer, lancez Xcode et créez un nouveau projet IOS App nommé GestureDemo. Nous utiliserons ce projet pour tester les exemples de code sur lesquels nous travaillerons.

Les gestes de base

Tap Gesture (Simple Tap et Double Tap)

Le tap est le geste le plus simple et le plus courant. Il se déclenche lorsqu’un utilisateur touche brièvement une vue.

Exemple d’un simple tap sur une Image :

struct ContentView: View {
    var body: some View {
        Image(systemName: "hand.point.right.fill")
            .gesture(
                TapGesture()
                    .onEnded { _ in
                        print("Tapped")
                    }
            )
    }
}

Explications :

Long Press Gesture

Un long press détecte lorsqu’une vue est touchée pendant une durée prolongée.

Exemple simple d’un appui long sur une Image :

struct ContentView: View {
    var body: some View {
        let longPress = LongPressGesture()
            .onEnded { _ in
                print("Long Press")
            }
        
        return Image(systemName: "hand.point.right.fill")
            .gesture(longPress)
    }
}

Explications :

Magnification Gesture (Pincement)

Le pincement (magnification) est utilisé pour détecter le zoom, c’est-à-dire l’écartement ou le rapprochement de deux doigts.

Exemple simple avec détection après fin du geste :

struct ContentView: View {
    var body: some View {
        let magnificationGesture =
            MagnificationGesture(minimumScaleDelta: 0)
            .onEnded { _ in
                print("Gesture Ended")
            }
        
        return Image(systemName: "hand.point.right.fill")
            .resizable()
            .font(.largeTitle)
            .gesture(magnificationGesture)
            .frame(width: 100, height: 90)
    }
}

Exemple avec mise à jour continue (onChanged) pour animer la taille de l’image :

struct ContentView: View {
  @State private var magnification: CGFloat = 1.0

  var body: some View {
      let magnificationGesture =
          MagnificationGesture(minimumScaleDelta: 0)
          .onChanged { value in
              self.magnification = value
          }
          .onEnded { _ in
              print("Gesture Ended")
          }
      
      return Image(systemName: "hand.point.right.fill")
          .resizable()
          .font(.largeTitle)
          .scaleEffect(magnification)
          .gesture(magnificationGesture)
          .frame(width: 100, height: 90)
  }
}

Explications :

Drag Gesture (Glissement)

Le drag (glissement) permet de déplacer une vue en suivant le mouvement du doigt.

Exemple simple d’un glissement :

struct ContentView: View {
  @GestureState private var offset: CGSize = .zero

  var body: some View {
      Image(systemName: "square.and.arrow.down")
          .offset(offset)
          .gesture(
              DragGesture()
                  .updating($offset) { dragValue, state, transaction in
                      state = dragValue.translation
                  }
          )
  }
}

Explications :

Composition de gestes

SwiftUI permet également de composer plusieurs gestes sur une même vue pour obtenir des interactions plus complexes.

Gestes simultanés

Pour reconnaître plusieurs gestes en même temps, utilisez le modificateur .simultaneously(with:).

Exemple : Combiner un appui long et un drag

struct ContentView: View {
  @GestureState private var offset: CGSize = .zero
  @GestureState private var longPressActive: Bool = false

  var body: some View {
      let longPressAndDrag = LongPressGesture(minimumDuration: 1.0)
          .updating($longPressActive) { value, state, transaction in
              state = value
          }
          .simultaneously(with: DragGesture())
          .updating($offset) { value, state, transaction in
              state = value.second?.translation ?? .zero
          }

      return Image(systemName: "hand.point.right.fill")
          .font(.largeTitle)
          .offset(offset)
          .gesture(longPressAndDrag)
  }
}

Explications :

Gestes séquentiels

Pour que le second geste ne se déclenche qu’après le premier, utilisez le modificateur .sequenced(before:).

Exemple : Un appui long suivi d’un drag

struct ContentView: View {
  @GestureState var offset: CGSize = .zero
  @State var dragEnabled = false

  var body: some View {
      let longPressBeforeDrag = LongPressGesture(minimumDuration: 2.0)
          .onEnded { _ in self.dragEnabled = true }
          .sequenced(before: DragGesture())
          .updating($offset) { value, state, transaction in
              switch value {
              case .first(true):
                  print("Long press in progress")
              case .second(true, let drag?):
                  state = drag.translation
              default:
                  break
              }
          }
          .onEnded { _ in self.dragEnabled = false }

      return Image(systemName: "hand.point.right.fill")
          .foregroundColor(dragEnabled ? .green : .blue)
          .font(.largeTitle)
          .offset(offset)
          .gesture(longPressBeforeDrag)
  }
}

Explications :

Gestes exclusifs

Pour que la détection de l’un des gestes empêche la détection simultanée des autres, utilisez ExclusiveGesture.

Exemple : Gérer un tap et un appui long sur la même vue

struct ContentView: View {
  @State private var message = "Tap ou Long Press"

  var body: some View {
      Text(message)
          .padding()
          .gesture(
              ExclusiveGesture(
                  TapGesture().onEnded { _ in
                      message = "Tap détecté !"
                  },
                  LongPressGesture().onEnded { _ in
                      message = "Appui long détecté !"
                  }
              )
          )
  }
}

Explications :