SwiftUI - Respond to tap AND double tap with different actions

Viewed 3897

I need to respond to a single tap and double tap with different actions, but is SwiftUI the double tap gesture is interpreted as two single taps.

In swift you can use fail gesture, but no idea how to do it in SwiftUI.

Example:

.onTapGesture(count: 1) {
     print("Single Tap!")
}
.onTapGesture(count: 2) {
     print("Double Tap!")
}

TIA.

6 Answers

First one prevent second one to perform. So reverse the order of your code:

.onTapGesture(count: 2) {
    print("Double Tap!")
}
.onTapGesture(count: 1) {
    print("Single Tap!")
}

Update: Second solution

Since the above method reported not working in some situationis, you can try using gestures modifier instead:

.gesture(TapGesture(count: 2).onEnded {
    print("double clicked")
})
.simultaneousGesture(TapGesture().onEnded {
    print("single clicked")
})
Text("Tap Me!").gesture(      
TapGesture(count: 2)
    .onEnded({ print("Tapped Twice!”)})
    .exclusively(before:
        TapGesture()
        .onEnded({print("Tapped Once!”) })          
)              

)

There does not seem to be a simple solution (yet), most of these answers trigger both actions. The workaround I have discovered feels clumsy, but works smoothly:

struct hwsView: View {
    var body: some View {
        VStack {
            Text("HWS")
                .onTapGesture {
                    print("Text single tapped")
                }
        }
        .highPriorityGesture(
            TapGesture(count: 2)
                .onEnded { _ in
                    print("hws double tapped")
                }
        )
    }
}

I had a need for double and triple taps to be recognised on an object but only trigger one of the action closures. I found that the exclusively() API didn't work for me on iOS 15. So, I created this little hack that does what I want.

Struct ContentView: View {
    @State private var isTripleTap = false
    
    var body: some View {
        Text("Button")
            .gesture(
                TapGesture(count: 2).onEnded {
                    Task {
                        try? await Task.sleep(nanoseconds: 200_000_000)
                        
                        if !isTripleTap {
                            print("Double Tap")
                        }
                        
                        isTripleTap = false
                    }
                }
                .simultaneously(
                    with: TapGesture(count: 3).onEnded {
                        isTripleTap = true
                        print("Triple Tap")
                    }
                )
            )
    }
}
Related