To having some multi gesture that fits every ones needs in projects, Apple has nothing offer than normal gesture, mixing them together to reach the wished gesture some times get tricky, here is a salvation, working without issue or bug!
A custom zero issue gesture called tapCountRecognizer, we can apply it to any View. For having single and double and triple tapGesture in the same time working together.
Notice that you can use like this as well, Just for doubleTap and tripleTap:
.tapCountRecognizer(tapSensitivity: 250, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
Or:
.tapCountRecognizer(doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)

import SwiftUI
struct ContentView: View {
var body: some View {
Circle()
.fill(Color.red)
.frame(width: 100, height: 100, alignment: .center)
.tapCountRecognizer(tapSensitivity: 250, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
.shadow(radius: 10)
.statusBar(hidden: true)
}
func singleTapAction() { print("singleTapAction") }
func doubleTapAction() { print("doubleTapAction") }
func tripleTapAction() { print("tripleTapAction") }
}
struct TapCountRecognizerModifier: ViewModifier {
let tapSensitivity: Int
let singleTapAction: (() -> Void)?
let doubleTapAction: (() -> Void)?
let tripleTapAction: (() -> Void)?
init(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) {
self.tapSensitivity = ((tapSensitivity >= 0) ? tapSensitivity : 250)
self.singleTapAction = singleTapAction
self.doubleTapAction = doubleTapAction
self.tripleTapAction = tripleTapAction
}
@State private var tapCount: Int = Int()
@State private var currentDispatchTimeID: DispatchTime = DispatchTime.now()
func body(content: Content) -> some View {
return content
.gesture(fundamentalGesture)
}
var fundamentalGesture: some Gesture {
DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
.onEnded() { _ in tapCount += 1; tapAnalyzerFunction() }
}
func tapAnalyzerFunction() {
currentDispatchTimeID = dispatchTimeIdGenerator(deadline: tapSensitivity)
if tapCount == 1 {
let singleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: singleTapGestureDispatchTimeID) {
if (singleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedSingleTapAction: () -> Void = singleTapAction { unwrappedSingleTapAction() }
tapCount = 0
}
}
}
else if tapCount == 2 {
let doubleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: doubleTapGestureDispatchTimeID) {
if (doubleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedDoubleTapAction: () -> Void = doubleTapAction { unwrappedDoubleTapAction() }
tapCount = 0
}
}
}
else {
if let unwrappedTripleTapAction: () -> Void = tripleTapAction { unwrappedTripleTapAction() }
tapCount = 0
}
}
func dispatchTimeIdGenerator(deadline: Int) -> DispatchTime { return DispatchTime.now() + DispatchTimeInterval.milliseconds(deadline) }
}
extension View {
func tapCountRecognizer(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) -> some View {
return self.modifier(TapCountRecognizerModifier(tapSensitivity: tapSensitivity, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction))
}
}