mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Convert the timeline's long press gesture recogniser to UIKit and prevent scroll view conflicts
This commit is contained in:
committed by
Stefan Ceriu
parent
b984c794fc
commit
ba031c8472
@@ -54,7 +54,7 @@ struct SwipeRightAction<Label: View>: ViewModifier {
|
|||||||
content
|
content
|
||||||
.offset(x: xOffset, y: 0.0)
|
.offset(x: xOffset, y: 0.0)
|
||||||
.animation(.interactiveSpring().speed(0.5), value: xOffset)
|
.animation(.interactiveSpring().speed(0.5), value: xOffset)
|
||||||
.gesture(PanGesture { gesture in
|
.gesture(PanGestureRepresentable { gesture in
|
||||||
switch gesture.state {
|
switch gesture.state {
|
||||||
case .ended, .cancelled, .failed:
|
case .ended, .cancelled, .failed:
|
||||||
if xOffset > actionThreshold {
|
if xOffset > actionThreshold {
|
||||||
@@ -204,7 +204,7 @@ struct SwipeRightAction_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
// Fixes the issue on iOS 18 where DragGesture conflicts with the scroll view
|
// Fixes the issue on iOS 18 where DragGesture conflicts with the scroll view
|
||||||
// https://github.com/feedback-assistant/reports/issues/542#issuecomment-2581322968
|
// https://github.com/feedback-assistant/reports/issues/542#issuecomment-2581322968
|
||||||
private struct PanGesture: UIGestureRecognizerRepresentable {
|
private struct PanGestureRepresentable: UIGestureRecognizerRepresentable {
|
||||||
var handle: (UIPanGestureRecognizer) -> Void
|
var handle: (UIPanGestureRecognizer) -> Void
|
||||||
|
|
||||||
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator { .init() }
|
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator { .init() }
|
||||||
|
|||||||
@@ -15,20 +15,30 @@ struct LongPressWithFeedback: ViewModifier {
|
|||||||
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
if #available(iOS 18, *) {
|
||||||
.compositingGroup() // Apply the shadow to the view as a whole.
|
mainContent(content: content)
|
||||||
.shadow(color: .black.opacity(isLongPressing ? 0.2 : 0.0), radius: isLongPressing ? 12 : 0)
|
.gesture(LongPressGestureRepresentable { gesture in
|
||||||
.shadow(color: .black.opacity(isLongPressing ? 0.1 : 0.0), radius: isLongPressing ? 3 : 0)
|
switch gesture.state {
|
||||||
.scaleEffect(x: isLongPressing ? 1.05 : 1,
|
case .ended, .cancelled, .failed:
|
||||||
y: isLongPressing ? 1.05 : 1)
|
handleLongPress(isPressing: false)
|
||||||
.animation(.spring(response: 0.7).delay(isLongPressing ? 0.1 : 0).disabledDuringTests(),
|
default:
|
||||||
value: isLongPressing)
|
handleLongPress(isPressing: true)
|
||||||
// The minimum duration here doesn't actually invoke the perform block when elapsed (thus
|
}
|
||||||
// the implementation below) but it does cancel other system gestures e.g. swipe to reply
|
})
|
||||||
|
} else {
|
||||||
|
mainContent(content: content)
|
||||||
.onLongPressGesture(minimumDuration: 0.25) { } onPressingChanged: { isPressing in
|
.onLongPressGesture(minimumDuration: 0.25) { } onPressingChanged: { isPressing in
|
||||||
|
handleLongPress(isPressing: isPressing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The gesture's minimum duration doesn't actually invoke the perform block when elapsed (thus
|
||||||
|
// the implementation below) but it does cancel other system gestures e.g. swipe to reply
|
||||||
|
private func handleLongPress(isPressing: Bool) {
|
||||||
isLongPressing = isPressing
|
isLongPressing = isPressing
|
||||||
|
|
||||||
guard isPressing else {
|
guard isLongPressing else {
|
||||||
triggerTask?.cancel()
|
triggerTask?.cancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -45,6 +55,17 @@ struct LongPressWithFeedback: ViewModifier {
|
|||||||
feedbackGenerator.impactOccurred()
|
feedbackGenerator.impactOccurred()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func mainContent(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.compositingGroup() // Apply the shadow to the view as a whole.
|
||||||
|
.shadow(color: .black.opacity(isLongPressing ? 0.2 : 0.0), radius: isLongPressing ? 12 : 0)
|
||||||
|
.shadow(color: .black.opacity(isLongPressing ? 0.1 : 0.0), radius: isLongPressing ? 3 : 0)
|
||||||
|
.scaleEffect(x: isLongPressing ? 1.05 : 1,
|
||||||
|
y: isLongPressing ? 1.05 : 1)
|
||||||
|
.animation(.spring(response: 0.7).delay(isLongPressing ? 0.1 : 0).disabledDuringTests(),
|
||||||
|
value: isLongPressing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,3 +122,34 @@ struct LongPressWithFeedback_Previews: PreviewProvider, TestablePreview {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixes the issue on iOS 18 where LongPress conflicts with the scroll view
|
||||||
|
// https://github.com/feedback-assistant/reports/issues/542#issuecomment-2581322968
|
||||||
|
private struct LongPressGestureRepresentable: UIGestureRecognizerRepresentable {
|
||||||
|
var handle: (UILongPressGestureRecognizer) -> Void
|
||||||
|
|
||||||
|
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator { .init() }
|
||||||
|
|
||||||
|
func makeUIGestureRecognizer(context: Context) -> UILongPressGestureRecognizer {
|
||||||
|
let gesture = UILongPressGestureRecognizer()
|
||||||
|
gesture.minimumPressDuration = 0.25
|
||||||
|
gesture.delegate = context.coordinator
|
||||||
|
gesture.isEnabled = true
|
||||||
|
return gesture
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUIGestureRecognizerAction(_ recognizer: UILongPressGestureRecognizer, context: Context) {
|
||||||
|
handle(recognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UIGestureRecognizerDelegate {
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
|
||||||
|
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user