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.
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.
Le tap est le geste le plus simple et le plus courant. Il se déclenche lorsqu’un utilisateur touche brièvement une vue.
struct ContentView: View {
var body: some View {
Image(systemName: "hand.point.right.fill")
.gesture(
TapGesture()
.onEnded { _ in
print("Tapped")
}
)
}
}
let doubleTap = TapGesture(count: 2)
.onEnded { _ in
print("Double Tapped")
}
En spécifiant count: 2, le geste ne sera reconnu que si l’utilisateur effectue deux taps successifs.
Un long press détecte lorsqu’une vue est touchée pendant une durée prolongée.
struct ContentView: View {
var body: some View {
let longPress = LongPressGesture()
.onEnded { _ in
print("Long Press")
}
return Image(systemName: "hand.point.right.fill")
.gesture(longPress)
}
}
let longPress = LongPressGesture(minimumDuration: 10, maximumDistance: 25)
.onEnded { _ in
print("Long Press")
}
Cela signifie que l’appui doit durer au moins 10 secondes et ne pas dépasser 25 points de déplacement hors de la vue pour être reconnu.
Le pincement (magnification) est utilisé pour détecter le zoom, c’est-à-dire l’écartement ou le rapprochement de deux doigts.
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)
}
}
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)
}
}
Le drag (glissement) permet de déplacer une vue en suivant le mouvement du doigt.
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
}
)
}
}
SwiftUI permet également de composer plusieurs gestes sur une même vue pour obtenir des interactions plus complexes.
Pour reconnaître plusieurs gestes en même temps, utilisez le modificateur .simultaneously(with:).
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)
}
}
Pour que le second geste ne se déclenche qu’après le premier, utilisez le modificateur .sequenced(before:).
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)
}
}
Pour que la détection de l’un des gestes empêche la détection simultanée des autres, utilisez ExclusiveGesture.
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é !"
}
)
)
}
}