From 826f32e64e6fca5849a7fc563c28a0a23be1e90e Mon Sep 17 00:00:00 2001 From: Riley <142751032+rileysay@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:08:19 +1000 Subject: [PATCH 1/2] Keep iOS button hit testing within its subtree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop the upward hit-test search at the button when no eligible target has been found. This prevents returning an ancestor outside the button’s subtree while keeping eligible descendants reachable. --- .../apple/RNGestureHandlerButton.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm index b480fa93e3..f71267f63a 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm @@ -1470,6 +1470,9 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event RNGHUIView *inner = [super hitTest:point withEvent:event]; while (inner && ![self shouldHandleTouch:inner atPoint:point]) { + if (inner == self) { + return nil; + } inner = inner.superview; } return inner; From 91414c3e7bb0be741824d19567c5d6fc11b31708 Mon Sep 17 00:00:00 2001 From: Riley <142751032+rileysay@users.noreply.github.com> Date: Tue, 8 Sep 2026 23:54:07 +1000 Subject: [PATCH 2/2] Preserve disabled button hit targets and guard input handling Stop hit-test traversal at the button itself instead of returning nil. Add disabled-state guards to touch tracking and macOS mouse handlers. --- .../apple/RNGestureHandlerButton.mm | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm index f71267f63a..a69a0123e2 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm @@ -1193,6 +1193,11 @@ - (void)mouseExited:(NSEvent *)event - (void)mouseDown:(NSEvent *)event { + if (!_userEnabled) { + return; + } + + _isTouchInsideBounds = YES; [self handleAnimatePressIn]; [super mouseDown:event]; @@ -1200,6 +1205,10 @@ - (void)mouseDown:(NSEvent *)event - (void)mouseUp:(NSEvent *)event { + if (!_userEnabled) { + return; + } + NSPoint locationInView = [self convertPoint:[event locationInWindow] fromView:nil]; _isHovered = NSPointInRect(locationInView, self.bounds); [self recordHoverSampleForMouseEvent:event]; @@ -1212,6 +1221,10 @@ - (void)mouseUp:(NSEvent *)event - (void)mouseDragged:(NSEvent *)event { + if (!_userEnabled) { + return; + } + NSPoint locationInWindow = [event locationInWindow]; NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil]; BOOL currentlyInside = NSPointInRect(locationInView, self.bounds); @@ -1244,6 +1257,10 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { + if (!_userEnabled) { + return NO; + } + _isTouchInsideBounds = YES; // A pencil's hover-out arrives just before touch-down but only schedules the // clear, so `_isHovered` still reflects the open hover. Under Reduce Motion @@ -1469,10 +1486,7 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event } RNGHUIView *inner = [super hitTest:point withEvent:event]; - while (inner && ![self shouldHandleTouch:inner atPoint:point]) { - if (inner == self) { - return nil; - } + while (inner && inner != self && ![self shouldHandleTouch:inner atPoint:point]) { inner = inner.superview; } return inner;