I'm trying to detect when a finger first makes contact with a view in SwiftUI. I could do this very easily with UIKit Events but can't figure this out in SwiftUI.
I've tried a DragGesture with minimum movement of 0 but it still won't change until your finger moves.
TapGesture will only work when you lift your finger and LongPressGesture will not trigger fast enough no matter what I set the parameters to.
DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged({ _ in print("down")})
LongPressGesture(minimumDuration: 0.01, maximumDistance: 100).onEnded({_ in print("down")})
I want to detect a touchDown event as soon as a finger makes contact with a view. Apple's default gestures have restrictions to either distance or time.
Update: This is not an issue anymore as Apple has seemed to update how DragGesture works or maybe I was experiencing a specific contextual bug.
If you combine the code from these two questions:
How to detect a tap gesture location in SwiftUI?
UITapGestureRecognizer - make it work on touch down, not touch up?
You can make something like this:
ZStack {
Text("Test")
TapView {
print("Tapped")
}
}
struct TapView: UIViewRepresentable {
var tappedCallback: (() -> Void)
func makeUIView(context: UIViewRepresentableContext<TapView>) -> TapView.UIViewType {
let v = UIView(frame: .zero)
let gesture = SingleTouchDownGestureRecognizer(target: context.coordinator,
action: #selector(Coordinator.tapped))
v.addGestureRecognizer(gesture)
return v
}
class Coordinator: NSObject {
var tappedCallback: (() -> Void)
init(tappedCallback: @escaping (() -> Void)) {
self.tappedCallback = tappedCallback
}
@objc func tapped(gesture:UITapGestureRecognizer) {
self.tappedCallback()
}
}
func makeCoordinator() -> TapView.Coordinator {
return Coordinator(tappedCallback:self.tappedCallback)
}
func updateUIView(_ uiView: UIView,
context: UIViewRepresentableContext<TapView>) {
}
}
class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if self.state == .possible {
self.state = .recognized
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
self.state = .failed
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
self.state = .failed
}
}
There's definitely some abstractions we can make so that the usage is more like the other SwiftUI Gestures, but this is a start. Hopefully Apple builds in support for this at some point.