I'd like SwiftUI DragGesture to start only when the gesture is in a specific direction (horizontal/vertical). Is it possible?
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 :
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("Left")
} else {
print("Right")
}
}
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
})
An example in which, at the start of the movement, a vertical or horizontal movement is determined after which the object moves only in this direction
struct Test: View {
@State private var offsetX = CGFloat.zero
@State private var offsetY = CGFloat.zero
var body: some View {
ZStack {
Divider()
Divider().rotationEffect(.degrees(90))
Divider().rotationEffect(.degrees(45))
Divider().rotationEffect(.degrees(-45))
VStack {
Spacer()
Text("offsetX: \(offsetX) offsetY: \(offsetY)")
.foregroundColor(.black.opacity(0.5))
.frame(maxWidth: .infinity)
.padding(.bottom, 18.0)
}
Rectangle()
.fill(.green)
.frame(width: 300.0, height: 200.0)
.cornerRadius(13.0)
.offset(x: offsetX, y: offsetY)
.gesture(drag)
}
}
@State private var horizontal = true
@State private var vertical = true
var drag: some Gesture {
DragGesture()
.onChanged { dg in
var x = dg.translation.width
var y = dg.translation.height
if ((x < 0 && y < 0 && x < y) || (x < 0 && y > 0 && -x > y) || (x > 0 && y < 0 && x > -y) || (x > 0 && y > 0 && x > y)) && self.horizontal && self.vertical {
self.horizontal = true
self.vertical = false
} else if vertical && horizontal {
self.horizontal = false
self.vertical = true
}
if self.horizontal {
self.offsetX = x
} else {
self.offsetY = y
}
}
.onEnded { _ in
withAnimation {
self.offsetX = .zero
self.offsetY = .zero
}
self.horizontal = true
self.vertical = true
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.