I would like to recognize a TapGesture
and LongPressGesture
on the same item. And it works fine, with the following exception: the LongPressGesture
alone responds after the duration I specify, which is 0.25 seconds, but when I combine it with the TapGesture
, it takes at least 1 second—I can't find a way to make it respond more quickly. Here is a demo:
And here is the code for it:
struct ContentView: View {
@State var message = ""
var body: some View {
Circle()
.fill(Color.yellow)
.frame(width: 150, height: 150)
.onTapGesture(count: 1) {
message = "TAP"
}
.onLongPressGesture(minimumDuration: 0.25) {
message = "LONG\nPRESS"
}
.overlay(Text(message)
.font(.title).bold()
.multilineTextAlignment(.center)
.allowsHitTesting(false))
}
}
Notice that it works fine except for the duration of the LongPress, which is much longer than 0.25 seconds.
Any ideas? Thanks in advance!
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!
Here a custom zero issue gesture called interactionReader, we can apply it to any View. for having LongPressGesture and TapGesture in the same time.
import SwiftUI
struct ContentView: View {
var body: some View {
Circle()
.fill(Color.yellow)
.frame(width: 150, height: 150)
.interactionReader(longPressSensitivity: 250, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: true)
.animation(Animation.easeInOut(duration: 0.2))
}
func tapAction() { print("tap action!") }
func longPressAction() { print("longPress action!") }
}
struct InteractionReaderViewModifier: ViewModifier {
var longPressSensitivity: Int
var tapAction: () -> Void
var longPressAction: () -> Void
var scaleEffect: Bool = true
@State private var isPressing: Bool = Bool()
@State private var currentDismissId: DispatchTime = DispatchTime.now()
@State private var lastInteractionKind: String = String()
func body(content: Content) -> some View {
let processedContent = content
.gesture(gesture)
.onChange(of: isPressing) { newValue in
currentDismissId = DispatchTime.now() + .milliseconds(longPressSensitivity)
let dismissId: DispatchTime = currentDismissId
if isPressing {
DispatchQueue.main.asyncAfter(deadline: dismissId) {
if isPressing { if (dismissId == currentDismissId) { lastInteractionKind = "longPress"; longPressAction() } }
}
}
else {
if (lastInteractionKind != "longPress") { lastInteractionKind = "tap"; tapAction() }
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {lastInteractionKind = "none"}
}
}
return Group {
if scaleEffect { processedContent.scaleEffect(lastInteractionKind == "longPress" ? 1.5: (lastInteractionKind == "tap" ? 0.8 : 1.0 )) }
else { processedContent }
}
}
var gesture: some Gesture {
DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
.onChanged() { _ in if !isPressing { isPressing = true } }
.onEnded() { _ in isPressing = false }
}
}
extension View {
func interactionReader(longPressSensitivity: Int, tapAction: @escaping () -> Void, longPressAction: @escaping () -> Void, scaleEffect: Bool = true) -> some View {
return self.modifier(InteractionReaderViewModifier(longPressSensitivity: longPressSensitivity, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: scaleEffect))
}
}