From a86adde59d236c38fac91ec8ae77ccbd9d56443b Mon Sep 17 00:00:00 2001 From: Joondong Date: Wed, 7 Oct 2026 01:44:49 +0900 Subject: [PATCH 1/2] [iOS] Fix JS touches being cancelled when a ScrollView wrapped in a native gesture starts scrolling A native handler bound to a ScrollView attaches its RNDummyGestureRecognizer to the component view that wraps the platform scroll view, so the scroll recognizer exception in gestureRecognizer:didActivateInViewWithTouchHandler: misses it and every JS responder is cancelled when scrolling starts. Skip the cancellation for that recognizer too, since it only mirrors the scroll view's pan recognizer. The class check keeps other handlers attached to the same view (for example a legacy Gesture.Pan() wrapping a ScrollView) cancelling JS responders as before. Co-Authored-By: Claude Opus 5.5 --- .../apple/RNGestureHandlerManager.mm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index ba06fea99f..95c00a8d81 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -377,6 +377,15 @@ - (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer return; } + // A native handler bound to a ScrollView is attached to the component view that wraps the platform + // scroll view, so the check above does not recognize it as a scroll recognizer. Its recognizer only + // mirrors the state of the scroll view's pan recognizer, so its activation means scrolling started. + RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:gestureRecognizer]; + if ([gestureRecognizer isKindOfClass:[RNDummyGestureRecognizer class]] && handler != nil && + [handler retrieveScrollView:gestureRecognizer.view] != nil) { + return; + } + UIGestureRecognizer *touchHandler = nil; // this way we can extract the touch handler on both architectures relatively easily From 92063e7577c10dcb344ed912db275325a06b40d9 Mon Sep 17 00:00:00 2001 From: Joondong Date: Wed, 7 Oct 2026 01:51:00 +0900 Subject: [PATCH 2/2] [iOS] Keep the native scroll recognizer exception off macOS On macOS RNDummyGestureRecognizer begins on mouse down instead of mirroring the scroll view's pan recognizer, so skipping the cancellation there would suppress it for every press inside a wrapped ScrollView. Co-Authored-By: Claude Opus 5.5 --- .../apple/RNGestureHandlerManager.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 95c00a8d81..cf8e034567 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -377,14 +377,17 @@ - (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer return; } +#if !TARGET_OS_OSX // A native handler bound to a ScrollView is attached to the component view that wraps the platform - // scroll view, so the check above does not recognize it as a scroll recognizer. Its recognizer only - // mirrors the state of the scroll view's pan recognizer, so its activation means scrolling started. + // scroll view, so the check above does not recognize it as a scroll recognizer. On iOS its recognizer + // only mirrors the state of the scroll view's pan recognizer, so its activation means scrolling started. + // On macOS it begins on mouse down, so it is not skipped there. RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:gestureRecognizer]; if ([gestureRecognizer isKindOfClass:[RNDummyGestureRecognizer class]] && handler != nil && [handler retrieveScrollView:gestureRecognizer.view] != nil) { return; } +#endif UIGestureRecognizer *touchHandler = nil;