I'd like SwiftUI DragGesture to start only when the gesture is in a specific direction (horizontal/vertical). Is it possible?
Rather old question but didn't find the answer elsewhere.
You can easily check wether it was a left- or a right-swipe, because Gestures return their end state, which contains the starting and final position.
Text("Hello")
.gesture(
DragGesture(minimumDistance: 100)
.onEnded { endedGesture in
if (endedGesture.location.x - endedGesture.startLocation.x) > 0 {
print("Right")
} else {
print("Left")
}
}
Yes, it is, by applying one of the two components (either horizontal or vertical) of the gesture translation to the view offset.
Here's such behaviour implemented as a ViewModifier
.
struct DraggableModifier : ViewModifier {
enum Direction {
case vertical
case horizontal
}
let direction: Direction
@State private var draggedOffset: CGSize = .zero
func body(content: Content) -> some View {
content
.offset(
CGSize(width: direction == .vertical ? 0 : draggedOffset.width,
height: direction == .horizontal ? 0 : draggedOffset.height)
)
.gesture(
DragGesture()
.onChanged { value in
self.draggedOffset = value.translation
}
.onEnded { value in
self.draggedOffset = .zero
}
)
}
}
Demo:
struct ContentView: View {
var body: some View {
VStack {
Spacer(minLength: 100)
HStack {
Rectangle()
.foregroundColor(.green)
.frame(width: 100, height: 100)
.modifier(DraggableModifier(direction: .vertical))
Text("Vertical")
}
Spacer(minLength: 100)
Rectangle()
.foregroundColor(.red)
.frame(width: 100, height: 100)
.modifier(DraggableModifier(direction: .horizontal))
Text(verbatim: "Horizontal")
Spacer(minLength: 100)
}
}
}
Result:
This will only move the view if the gesture moves move horizontally than vertically. The isDragging boolean makes sure after the first condition is met, the view will move vertically too. It's not necessary, but it helps for smoother dragging.
@State var dragOffset: CGSize = .zero
@State var isDragging = false
.offset(x: offset.width, y: offset.height)
.gesture(DragGesture()
.onChanged { if abs($0.translation.width) > abs($0.translation.height) || isDragging {
self.offset = $0.translation
self.isDragging = true
}
}
.onEnded {
isDragging = false
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With