swiftui

SwiftUI DragGesture only in one direction


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


Solution

  • 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