Reputation: 5645
I am trying to distinguish double and triple tap gesture recognisers in SwiftUI. In storyboard-based application it can be done using UIGestureRecognizerDelegate
, but in SwiftUI I haven't found any solutions.
NOTE:
The following doesn't work:
.onTapGesture(count: 3) {
print("Triple Tap!")
}
.onTapGesture(count: 2) {
print("Double Tap!")
}
Upvotes: 9
Views: 2345
Reputation: 2104
Tested on iOS 14.4.2, Xcode 12.5 beta 3.
Using this tapGesture, after only one tap nothing happens, after double tap you'll see "double tap"
printed in console, and after triple or more tap you'll see "triple tap"
printed in console.
struct ContentView: View {
var tapGesture: some Gesture {
SimultaneousGesture(TapGesture(count: 2), TapGesture(count: 3))
.onEnded { gestureValue in
if gestureValue.second != nil {
print("triple tap!")
} else if gestureValue.first != nil {
print("double tap!")
}
}
}
var body: some View {
Text("Hello World!")
.gesture(tapGesture)
}
}
Upvotes: 8
Reputation: 1
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))
}
}
Upvotes: 5
Reputation: 102
For this, you can use .gesture
with .simultaneously(with:)
. From the documentation: Combines a gesture with another gesture to create a new gesture that recognizes both gestures at the same time.
And example code is
struct TwoGesturesView: View {
var body: some View {
Text("Hello World!")
.gesture(TapGesture(count: 2).onEnded({
print("Double Tap")
})
.simultaneously(with: TapGesture(count: 3).onEnded({
print("Triple Tap")
})))
}
}
Upvotes: 0
Reputation: 29676
import SwiftUI
struct MultipleTaps: View {
let doubleTap = TapGesture(count: 2).onEnded({print("Double Tap!")})
let tripleTap = TapGesture(count: 3).onEnded({print("Triple Tap!")})
var body: some View {
let tripleBeforedouble = tripleTap.exclusively(before: doubleTap)
return Text("Hello World!").gesture(tripleBeforedouble)
}
}
struct MultipleTaps_Previews: PreviewProvider {
static var previews: some View {
MultipleTaps()
}
}
Upvotes: 1