Perhaps this will be useful to someone.
We made custom gestures for iOS 18 using the new UIGestureRecogniserRepresentable protocol. This gesture avoids locking scrolling in ScrollView if the user puts their finger on an item using this custom gesture. But at the same time activate the drag gesture if the user does not scroll and long presses on a block using this gesture, at which point it blocks the drag action of the ScrollView (as opposed to using .simultaneousGesture()).
And yes, as noted above, it's not as nice as the naive SwiftUI.
struct DragGestureWithDelayRecognizer: UIGestureRecognizerRepresentable {
let delay: TimeInterval
let onChanged: (CGPoint) -> Void
let onEnded: () -> Void
func makeUIGestureRecognizer(context: Context) -> some UIGestureRecognizer {
let recognizer = UILongPressGestureRecognizer(target: context.coordinator,
action: #selector(Coordinator.handleGesture(_:)))
recognizer.minimumPressDuration = delay
recognizer.allowableMovement = .infinity // so that it doesn't get cancelled when moving
recognizer.delegate = context.coordinator
return recognizer
}
func handleUIGestureRecognizerAction(_ recognizer: UIGestureRecognizerType, context: Context) {}
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator {
return Coordinator(converter: converter, onChanged: onChanged, onEnded: onEnded)
}
final class Coordinator: NSObject, UIGestureRecognizerDelegate {
let converter: CoordinateSpaceConverter
let onChanged: (CGPoint) -> Void
let onEnded: () -> Void
init(converter: CoordinateSpaceConverter,
onChanged: @escaping (CGPoint) -> Void,
onEnded: @escaping () -> Void) {
self.converter = converter
self.onChanged = onChanged
self.onEnded = onEnded
}
@objc func handleGesture(_ recognizer: UILongPressGestureRecognizer) {
let location = recognizer.location(in: recognizer.view)
let swiftUILocation = converter.convert(globalPoint: location)
switch recognizer.state {
case .began, .changed:
onChanged(swiftUILocation)
case .ended, .cancelled, .failed:
onEnded()
default:
break
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: