Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI DragGesture only in one direction

Tags:

swiftui

I'd like SwiftUI DragGesture to start only when the gesture is in a specific direction (horizontal/vertical). Is it possible?

like image 430
VojtaStavik Avatar asked Oct 04 '19 13:10

VojtaStavik


3 Answers

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")
                }
            }
like image 194
Fnöh Avatar answered Oct 11 '22 08:10

Fnöh


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:

enter image description here

like image 39
Matteo Pacini Avatar answered Oct 11 '22 10:10

Matteo Pacini


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
                        
                    })
like image 39
Tweety Avatar answered Oct 11 '22 09:10

Tweety