From af1f77c858085ff3bfecb2b8becaaf4061811e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Wed, 5 Aug 2026 19:04:58 +0200 Subject: [PATCH 1/2] Track current pointer --- .../apple/RNGestureHandlerPointerTracker.mm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm index 2f06e15dbc..db4e3b3a3d 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm @@ -39,7 +39,14 @@ - (int)registerTouch:(RNGHUITouch *)touch - (int)unregisterTouch:(RNGHUITouch *)touch { for (int index = 0; index < MAX_POINTERS_COUNT; index++) { +#if TARGET_OS_OSX + // A macOS mouse sequence delivers a fresh NSEvent per event, so identity matching + // (valid on iOS, where a UITouch is one stable object for the whole touch) never + // matches. There is only one mouse pointer — match the tracked slot instead. + if (_trackedPointers[index] != nil) { +#else if (_trackedPointers[index] == touch) { +#endif _trackedPointers[index] = nil; return index; } @@ -51,7 +58,12 @@ - (int)unregisterTouch:(RNGHUITouch *)touch - (int)findTouchIndex:(RNGHUITouch *)touch { for (int index = 0; index < MAX_POINTERS_COUNT; index++) { +#if TARGET_OS_OSX + // See unregisterTouch: — identity matching cannot work for NSEvents. + if (_trackedPointers[index] != nil) { +#else if (_trackedPointers[index] == touch) { +#endif return index; } } @@ -159,6 +171,12 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event continue; } +#if TARGET_OS_OSX + // Replace the stored mouse-down event with the latest one so extractAllTouches + // reads the pointer's current position, not where the sequence started. + _trackedPointers[index] = touch; +#endif + data[changedCount++] = [self extractPointerData:index forTouch:touch]; } From d18135580a78a1eec10bf5a519a3ad69bac0656b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Wed, 5 Aug 2026 19:21:00 +0200 Subject: [PATCH 2/2] Refresh tracked pointers --- .../apple/RNGestureHandlerPointerTracker.mm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm index db4e3b3a3d..cd0d70155a 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerPointerTracker.mm @@ -195,6 +195,18 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event return; } +#if TARGET_OS_OSX + // Refresh the tracked slot with the ending event so extractAllTouches below reports + // the pointer's final position. On iOS the stored UITouch is a live object that + // always reads its current location; a stored NSEvent is a stale snapshot. + for (RNGHUITouch *touch in touches) { + int index = [self findTouchIndex:touch]; + if (index >= 0) { + _trackedPointers[index] = touch; + } + } +#endif + // extract all touches first to include the ones that were just lifted [self extractAllTouches];