react-native-gesture-handler 2.32.0 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/android/build.gradle +27 -11
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/java/com/swmansion/gesturehandler/core/FlingGestureHandler.kt +1 -1
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +32 -33
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +144 -15
  6. package/android/src/main/java/com/swmansion/gesturehandler/core/LongPressGestureHandler.kt +6 -3
  7. package/android/src/main/java/com/swmansion/gesturehandler/core/NativeViewGestureHandler.kt +23 -0
  8. package/android/src/main/java/com/swmansion/gesturehandler/core/PanGestureHandler.kt +4 -7
  9. package/android/src/main/java/com/swmansion/gesturehandler/core/TapGestureHandler.kt +1 -1
  10. package/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt +3 -0
  11. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +2 -0
  12. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt +9 -0
  13. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootView.kt +8 -6
  14. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerTouchEvent.kt +2 -7
  15. package/apple/Handlers/RNFlingHandler.m +44 -4
  16. package/apple/Handlers/RNHoverHandler.m +24 -26
  17. package/apple/Handlers/RNLongPressHandler.m +4 -0
  18. package/apple/Handlers/RNPanHandler.m +44 -7
  19. package/apple/Handlers/RNTapHandler.m +35 -4
  20. package/apple/RNGHUIKit.h +10 -10
  21. package/apple/RNGestureHandler.h +2 -0
  22. package/apple/RNGestureHandler.mm +47 -6
  23. package/apple/RNGestureHandlerButton.mm +5 -3
  24. package/apple/RNGestureHandlerManager.mm +63 -6
  25. package/apple/RNGestureHandlerModule.mm +20 -0
  26. package/apple/RNGestureHandlerPointerTracker.m +64 -10
  27. package/apple/RNGestureHandlerPointerType.h +1 -0
  28. package/apple/RNManualActivationRecognizer.m +25 -6
  29. package/lib/commonjs/components/GestureComponents.js +21 -1
  30. package/lib/commonjs/components/GestureComponents.js.map +1 -1
  31. package/lib/commonjs/components/GestureHandlerButton.web.js +2 -1
  32. package/lib/commonjs/components/GestureHandlerButton.web.js.map +1 -1
  33. package/lib/commonjs/components/Pressable/Pressable.js +28 -3
  34. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  35. package/lib/commonjs/components/Pressable/index.js.map +1 -1
  36. package/lib/commonjs/components/ReanimatedDrawerLayout.js +4 -2
  37. package/lib/commonjs/components/ReanimatedDrawerLayout.js.map +1 -1
  38. package/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js +32 -9
  39. package/lib/commonjs/components/ReanimatedSwipeable/ReanimatedSwipeable.js.map +1 -1
  40. package/lib/commonjs/components/Swipeable.js.map +1 -1
  41. package/lib/commonjs/components/Text.js.map +1 -1
  42. package/lib/commonjs/components/touchables/TouchableHighlight.js +1 -1
  43. package/lib/commonjs/components/touchables/TouchableHighlight.js.map +1 -1
  44. package/lib/commonjs/components/touchables/TouchableNativeFeedback.android.js.map +1 -1
  45. package/lib/commonjs/components/touchables/TouchableOpacity.js +1 -0
  46. package/lib/commonjs/components/touchables/TouchableOpacity.js.map +1 -1
  47. package/lib/commonjs/getShadowNodeFromRef.js +6 -2
  48. package/lib/commonjs/getShadowNodeFromRef.js.map +1 -1
  49. package/lib/commonjs/handlers/PanGestureHandler.js.map +1 -1
  50. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.js +56 -11
  51. package/lib/commonjs/handlers/gestures/GestureDetector/Wrap.js.map +1 -1
  52. package/lib/commonjs/handlers/gestures/GestureDetector/index.js +2 -1
  53. package/lib/commonjs/handlers/gestures/GestureDetector/index.js.map +1 -1
  54. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -1
  55. package/lib/commonjs/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  56. package/lib/commonjs/handlers/gestures/GestureDetector/useMountReactions.js +6 -0
  57. package/lib/commonjs/handlers/gestures/GestureDetector/useMountReactions.js.map +1 -1
  58. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js +3 -2
  59. package/lib/commonjs/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  60. package/lib/commonjs/handlers/gestures/gestureStateManager.web.js.map +1 -1
  61. package/lib/commonjs/handlers/gestures/panGesture.js +6 -3
  62. package/lib/commonjs/handlers/gestures/panGesture.js.map +1 -1
  63. package/lib/commonjs/hostInstance.js +39 -0
  64. package/lib/commonjs/hostInstance.js.map +1 -0
  65. package/lib/commonjs/index.js.map +1 -1
  66. package/lib/commonjs/mocks/mocks.js +1 -0
  67. package/lib/commonjs/mocks/mocks.js.map +1 -1
  68. package/lib/commonjs/specs/NativeRNGestureHandlerModule.js.map +1 -1
  69. package/lib/commonjs/specs/RNGestureHandlerButtonNativeComponent.ts +16 -14
  70. package/lib/commonjs/specs/RNGestureHandlerRootViewNativeComponent.ts +3 -3
  71. package/lib/commonjs/web/detectors/RotationGestureDetector.js +1 -1
  72. package/lib/commonjs/web/handlers/LongPressGestureHandler.js +3 -1
  73. package/lib/commonjs/web/handlers/LongPressGestureHandler.js.map +1 -1
  74. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js +9 -1
  75. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js.map +1 -1
  76. package/lib/commonjs/web/handlers/PanGestureHandler.js +8 -9
  77. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  78. package/lib/commonjs/web/handlers/TapGestureHandler.js +1 -1
  79. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js +14 -0
  80. package/lib/commonjs/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  81. package/lib/commonjs/web/tools/WheelEventManager.js +4 -0
  82. package/lib/commonjs/web/tools/WheelEventManager.js.map +1 -1
  83. package/lib/module/components/GestureComponents.js +23 -2
  84. package/lib/module/components/GestureComponents.js.map +1 -1
  85. package/lib/module/components/GestureHandlerButton.web.js +2 -1
  86. package/lib/module/components/GestureHandlerButton.web.js.map +1 -1
  87. package/lib/module/components/Pressable/Pressable.js +28 -3
  88. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  89. package/lib/module/components/Pressable/index.js.map +1 -1
  90. package/lib/module/components/ReanimatedDrawerLayout.js +4 -2
  91. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -1
  92. package/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js +34 -11
  93. package/lib/module/components/ReanimatedSwipeable/ReanimatedSwipeable.js.map +1 -1
  94. package/lib/module/components/Swipeable.js.map +1 -1
  95. package/lib/module/components/Text.js.map +1 -1
  96. package/lib/module/components/touchables/TouchableHighlight.js +2 -2
  97. package/lib/module/components/touchables/TouchableHighlight.js.map +1 -1
  98. package/lib/module/components/touchables/TouchableNativeFeedback.android.js.map +1 -1
  99. package/lib/module/components/touchables/TouchableOpacity.js +1 -0
  100. package/lib/module/components/touchables/TouchableOpacity.js.map +1 -1
  101. package/lib/module/getShadowNodeFromRef.js +7 -2
  102. package/lib/module/getShadowNodeFromRef.js.map +1 -1
  103. package/lib/module/handlers/PanGestureHandler.js.map +1 -1
  104. package/lib/module/handlers/gestures/GestureDetector/Wrap.js +56 -11
  105. package/lib/module/handlers/gestures/GestureDetector/Wrap.js.map +1 -1
  106. package/lib/module/handlers/gestures/GestureDetector/index.js +2 -1
  107. package/lib/module/handlers/gestures/GestureDetector/index.js.map +1 -1
  108. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js +2 -1
  109. package/lib/module/handlers/gestures/GestureDetector/useDetectorUpdater.js.map +1 -1
  110. package/lib/module/handlers/gestures/GestureDetector/useMountReactions.js +6 -0
  111. package/lib/module/handlers/gestures/GestureDetector/useMountReactions.js.map +1 -1
  112. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js +3 -2
  113. package/lib/module/handlers/gestures/GestureDetector/useViewRefHandler.js.map +1 -1
  114. package/lib/module/handlers/gestures/gestureStateManager.web.js.map +1 -1
  115. package/lib/module/handlers/gestures/panGesture.js +6 -3
  116. package/lib/module/handlers/gestures/panGesture.js.map +1 -1
  117. package/lib/module/hostInstance.js +32 -0
  118. package/lib/module/hostInstance.js.map +1 -0
  119. package/lib/module/index.js.map +1 -1
  120. package/lib/module/mocks/mocks.js +3 -2
  121. package/lib/module/mocks/mocks.js.map +1 -1
  122. package/lib/module/specs/NativeRNGestureHandlerModule.js.map +1 -1
  123. package/lib/module/specs/RNGestureHandlerButtonNativeComponent.ts +16 -14
  124. package/lib/module/specs/RNGestureHandlerRootViewNativeComponent.ts +3 -3
  125. package/lib/module/web/detectors/RotationGestureDetector.js +1 -1
  126. package/lib/module/web/handlers/LongPressGestureHandler.js +3 -1
  127. package/lib/module/web/handlers/LongPressGestureHandler.js.map +1 -1
  128. package/lib/module/web/handlers/NativeViewGestureHandler.js +9 -1
  129. package/lib/module/web/handlers/NativeViewGestureHandler.js.map +1 -1
  130. package/lib/module/web/handlers/PanGestureHandler.js +8 -9
  131. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  132. package/lib/module/web/handlers/TapGestureHandler.js +1 -1
  133. package/lib/module/web/tools/GestureHandlerOrchestrator.js +14 -0
  134. package/lib/module/web/tools/GestureHandlerOrchestrator.js.map +1 -1
  135. package/lib/module/web/tools/WheelEventManager.js +4 -0
  136. package/lib/module/web/tools/WheelEventManager.js.map +1 -1
  137. package/lib/typescript/components/GestureButtons.d.ts +3 -1
  138. package/lib/typescript/components/GestureButtons.d.ts.map +1 -1
  139. package/lib/typescript/components/GestureButtonsProps.d.ts +10 -0
  140. package/lib/typescript/components/GestureButtonsProps.d.ts.map +1 -1
  141. package/lib/typescript/components/GestureComponents.d.ts +8 -7
  142. package/lib/typescript/components/GestureComponents.d.ts.map +1 -1
  143. package/lib/typescript/components/GestureComponents.web.d.ts +12 -4
  144. package/lib/typescript/components/GestureComponents.web.d.ts.map +1 -1
  145. package/lib/typescript/components/GestureHandlerButton.web.d.ts +2 -2
  146. package/lib/typescript/components/GestureHandlerButton.web.d.ts.map +1 -1
  147. package/lib/typescript/components/Pressable/Pressable.d.ts.map +1 -1
  148. package/lib/typescript/components/Pressable/PressableProps.d.ts +1 -1
  149. package/lib/typescript/components/Pressable/PressableProps.d.ts.map +1 -1
  150. package/lib/typescript/components/Pressable/index.d.ts +1 -1
  151. package/lib/typescript/components/Pressable/index.d.ts.map +1 -1
  152. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts.map +1 -1
  153. package/lib/typescript/components/ReanimatedSwipeable/ReanimatedSwipeable.d.ts.map +1 -1
  154. package/lib/typescript/components/Swipeable.d.ts.map +1 -1
  155. package/lib/typescript/components/touchables/TouchableHighlight.d.ts +1 -1
  156. package/lib/typescript/components/touchables/TouchableHighlight.d.ts.map +1 -1
  157. package/lib/typescript/components/touchables/TouchableNativeFeedback.android.d.ts +1 -1
  158. package/lib/typescript/components/touchables/TouchableNativeFeedback.android.d.ts.map +1 -1
  159. package/lib/typescript/components/touchables/TouchableOpacity.d.ts.map +1 -1
  160. package/lib/typescript/getShadowNodeFromRef.d.ts.map +1 -1
  161. package/lib/typescript/handlers/PanGestureHandler.d.ts +12 -0
  162. package/lib/typescript/handlers/PanGestureHandler.d.ts.map +1 -1
  163. package/lib/typescript/handlers/gestures/GestureDetector/Wrap.d.ts +11 -0
  164. package/lib/typescript/handlers/gestures/GestureDetector/Wrap.d.ts.map +1 -1
  165. package/lib/typescript/handlers/gestures/GestureDetector/index.d.ts.map +1 -1
  166. package/lib/typescript/handlers/gestures/GestureDetector/useDetectorUpdater.d.ts.map +1 -1
  167. package/lib/typescript/handlers/gestures/GestureDetector/useMountReactions.d.ts.map +1 -1
  168. package/lib/typescript/handlers/gestures/GestureDetector/useViewRefHandler.d.ts.map +1 -1
  169. package/lib/typescript/handlers/gestures/gestureStateManager.web.d.ts +8 -1
  170. package/lib/typescript/handlers/gestures/gestureStateManager.web.d.ts.map +1 -1
  171. package/lib/typescript/handlers/gestures/panGesture.d.ts +6 -3
  172. package/lib/typescript/handlers/gestures/panGesture.d.ts.map +1 -1
  173. package/lib/typescript/hostInstance.d.ts +10 -0
  174. package/lib/typescript/hostInstance.d.ts.map +1 -0
  175. package/lib/typescript/index.d.ts +1 -1
  176. package/lib/typescript/index.d.ts.map +1 -1
  177. package/lib/typescript/mocks/mocks.d.ts +67 -15
  178. package/lib/typescript/mocks/mocks.d.ts.map +1 -1
  179. package/lib/typescript/specs/NativeRNGestureHandlerModule.d.ts +6 -7
  180. package/lib/typescript/specs/NativeRNGestureHandlerModule.d.ts.map +1 -1
  181. package/lib/typescript/specs/RNGestureHandlerButtonNativeComponent.d.ts +9 -10
  182. package/lib/typescript/specs/RNGestureHandlerButtonNativeComponent.d.ts.map +1 -1
  183. package/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts +2 -2
  184. package/lib/typescript/specs/RNGestureHandlerRootViewNativeComponent.d.ts.map +1 -1
  185. package/lib/typescript/web/handlers/LongPressGestureHandler.d.ts.map +1 -1
  186. package/lib/typescript/web/handlers/NativeViewGestureHandler.d.ts +1 -0
  187. package/lib/typescript/web/handlers/NativeViewGestureHandler.d.ts.map +1 -1
  188. package/lib/typescript/web/handlers/PanGestureHandler.d.ts.map +1 -1
  189. package/lib/typescript/web/tools/GestureHandlerOrchestrator.d.ts +1 -0
  190. package/lib/typescript/web/tools/GestureHandlerOrchestrator.d.ts.map +1 -1
  191. package/lib/typescript/web/tools/WheelEventManager.d.ts.map +1 -1
  192. package/package.json +7 -7
  193. package/src/components/GestureButtonsProps.ts +14 -0
  194. package/src/components/GestureComponents.tsx +38 -7
  195. package/src/components/GestureHandlerButton.web.tsx +7 -3
  196. package/src/components/Pressable/Pressable.tsx +33 -4
  197. package/src/components/Pressable/PressableProps.tsx +1 -1
  198. package/src/components/Pressable/index.ts +1 -0
  199. package/src/components/ReanimatedDrawerLayout.tsx +4 -1
  200. package/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx +49 -10
  201. package/src/components/Swipeable.tsx +6 -2
  202. package/src/components/Text.tsx +3 -3
  203. package/src/components/touchables/TouchableHighlight.tsx +4 -2
  204. package/src/components/touchables/TouchableNativeFeedback.android.tsx +8 -2
  205. package/src/components/touchables/TouchableOpacity.tsx +1 -0
  206. package/src/getShadowNodeFromRef.ts +11 -4
  207. package/src/handlers/PanGestureHandler.ts +15 -0
  208. package/src/handlers/gestures/GestureDetector/Wrap.tsx +78 -13
  209. package/src/handlers/gestures/GestureDetector/index.tsx +4 -1
  210. package/src/handlers/gestures/GestureDetector/useDetectorUpdater.ts +4 -1
  211. package/src/handlers/gestures/GestureDetector/useMountReactions.ts +6 -0
  212. package/src/handlers/gestures/GestureDetector/useViewRefHandler.ts +5 -2
  213. package/src/handlers/gestures/gestureStateManager.web.ts +9 -1
  214. package/src/handlers/gestures/panGesture.ts +6 -3
  215. package/src/hostInstance.ts +57 -0
  216. package/src/index.ts +1 -0
  217. package/src/mocks/mocks.tsx +6 -2
  218. package/src/specs/NativeRNGestureHandlerModule.ts +16 -10
  219. package/src/specs/RNGestureHandlerButtonNativeComponent.ts +16 -14
  220. package/src/specs/RNGestureHandlerRootViewNativeComponent.ts +3 -3
  221. package/src/web/detectors/RotationGestureDetector.ts +1 -1
  222. package/src/web/handlers/LongPressGestureHandler.ts +3 -1
  223. package/src/web/handlers/NativeViewGestureHandler.ts +11 -2
  224. package/src/web/handlers/PanGestureHandler.ts +8 -11
  225. package/src/web/handlers/TapGestureHandler.ts +1 -1
  226. package/src/web/tools/GestureHandlerOrchestrator.ts +19 -0
  227. package/src/web/tools/WheelEventManager.ts +1 -0
@@ -95,7 +95,8 @@
95
95
  #else
96
96
 
97
97
  @interface RNBetterSwipeGestureRecognizer : NSGestureRecognizer {
98
- dispatch_block_t failFlingAction;
98
+ // scheduled on mouseDown; fails the gesture if the fling doesn't complete within maxDuration
99
+ dispatch_block_t maxDurationTimeout;
99
100
  int maxDuration;
100
101
  int minVelocity;
101
102
  double defaultAlignmentCone;
@@ -137,18 +138,34 @@
137
138
  [_gestureHandler handleGesture:self];
138
139
  }
139
140
 
141
+ - (void)triggerAction
142
+ {
143
+ [_gestureHandler handleGesture:self fromReset:NO];
144
+ }
145
+
146
+ - (void)triggerActionFromReset
147
+ {
148
+ [_gestureHandler handleGesture:self fromReset:YES];
149
+ }
150
+
140
151
  - (void)mouseDown:(NSEvent *)event
141
152
  {
153
+ [_gestureHandler setCurrentPointerTypeToMouse];
154
+ [_gestureHandler reset];
142
155
  [super mouseDown:event];
143
156
 
144
157
  startPosition = [self locationInView:self.view];
145
158
  startTime = CACurrentMediaTime();
146
159
 
160
+ [_gestureHandler.pointerTracker touchesBegan:[NSSet setWithObject:event] withEvent:event];
161
+ // Send the BEGAN event, mirroring what the iOS recognizer does in touchesBegan.
162
+ [self triggerAction];
163
+
147
164
  self.state = NSGestureRecognizerStatePossible;
148
165
 
149
166
  __weak typeof(self) weakSelf = self;
150
167
 
151
- failFlingAction = dispatch_block_create(0, ^{
168
+ maxDurationTimeout = dispatch_block_create(0, ^{
152
169
  __strong typeof(self) strongSelf = weakSelf;
153
170
 
154
171
  if (strongSelf) {
@@ -159,13 +176,15 @@
159
176
  dispatch_after(
160
177
  dispatch_time(DISPATCH_TIME_NOW, (int64_t)(maxDuration * NSEC_PER_SEC)),
161
178
  dispatch_get_main_queue(),
162
- failFlingAction);
179
+ maxDurationTimeout);
163
180
  }
164
181
 
165
182
  - (void)mouseDragged:(NSEvent *)event
166
183
  {
167
184
  [super mouseDragged:event];
168
185
 
186
+ [_gestureHandler.pointerTracker touchesMoved:[NSSet setWithObject:event] withEvent:event];
187
+
169
188
  NSPoint currentPosition = [self locationInView:self.view];
170
189
  double currentTime = CACurrentMediaTime();
171
190
 
@@ -180,16 +199,37 @@
180
199
  [self tryActivate:velocityVector];
181
200
  }
182
201
 
202
+ - (void)cancelMaxDurationTimeout
203
+ {
204
+ if (maxDurationTimeout != nil) {
205
+ dispatch_block_cancel(maxDurationTimeout);
206
+ maxDurationTimeout = nil;
207
+ }
208
+ }
209
+
183
210
  - (void)mouseUp:(NSEvent *)event
184
211
  {
185
212
  [super mouseUp:event];
186
213
 
187
- dispatch_block_cancel(failFlingAction);
214
+ [_gestureHandler.pointerTracker touchesEnded:[NSSet setWithObject:event] withEvent:event];
215
+
216
+ [self cancelMaxDurationTimeout];
188
217
 
189
218
  self.state =
190
219
  self.state == NSGestureRecognizerStateChanged ? NSGestureRecognizerStateEnded : NSGestureRecognizerStateFailed;
191
220
  }
192
221
 
222
+ - (void)reset
223
+ {
224
+ [self triggerActionFromReset];
225
+ [_gestureHandler.pointerTracker reset];
226
+ // The gesture may end without a mouse-up (view unmount, cancellation) — a still-scheduled
227
+ // timeout would fire later and set the state to Failed during the next gesture.
228
+ [self cancelMaxDurationTimeout];
229
+ [super reset];
230
+ [_gestureHandler reset];
231
+ }
232
+
193
233
  - (void)tryActivate:(Vector *)velocityVector
194
234
  {
195
235
  bool isAligned = NO;
@@ -132,8 +132,9 @@ API_AVAILABLE(ios(13.4))
132
132
  {
133
133
  #if CHECK_TARGET(13_4)
134
134
  if (@available(iOS 13.4, *)) {
135
- [super unbindFromView];
135
+ // Remove the interaction before [super unbindFromView] detaches the recognizer and nils recognizer.view.
136
136
  [self.recognizer.view removeInteraction:_pointerInteraction];
137
+ [super unbindFromView];
137
138
  }
138
139
  #endif
139
140
  }
@@ -218,33 +219,30 @@ API_AVAILABLE(ios(13.4))
218
219
  _view = nil;
219
220
  }
220
221
 
222
+ - (RNGestureHandlerEventExtraData *)extraDataForEvent:(NSEvent *)event
223
+ {
224
+ CGPoint windowLocation = [event locationInWindow];
225
+ CGPoint relativePos = [_view convertPoint:windowLocation fromView:nil];
226
+ CGPoint absolutePos = [_view.window.contentView convertPoint:windowLocation fromView:nil];
227
+
228
+ return [RNGestureHandlerEventExtraData forPosition:relativePos
229
+ withAbsolutePosition:absolutePos
230
+ withNumberOfTouches:1
231
+ withPointerType:_pointerType];
232
+ }
233
+
221
234
  - (void)mouseEntered:(NSEvent *)event
222
235
  {
223
- [self sendEventsInState:RNGestureHandlerStateBegan
224
- forViewWithTag:_view.reactTag
225
- withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
226
- withNumberOfTouches:1
227
- withPointerType:_pointerType]];
228
- [self sendEventsInState:RNGestureHandlerStateActive
229
- forViewWithTag:_view.reactTag
230
- withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
231
- withNumberOfTouches:1
232
- withPointerType:_pointerType]];
233
- }
234
-
235
- - (void)mouseExited:(NSEvent *)theEvent
236
- {
237
- [self sendEventsInState:RNGestureHandlerStateEnd
238
- forViewWithTag:_view.reactTag
239
- withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO
240
- withNumberOfTouches:1
241
- withPointerType:_pointerType]];
242
-
243
- [self sendEventsInState:RNGestureHandlerStateUndetermined
244
- forViewWithTag:_view.reactTag
245
- withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO
246
- withNumberOfTouches:1
247
- withPointerType:_pointerType]];
236
+ RNGestureHandlerEventExtraData *extraData = [self extraDataForEvent:event];
237
+ [self sendEventsInState:RNGestureHandlerStateBegan forViewWithTag:_view.reactTag withExtraData:extraData];
238
+ [self sendEventsInState:RNGestureHandlerStateActive forViewWithTag:_view.reactTag withExtraData:extraData];
239
+ }
240
+
241
+ - (void)mouseExited:(NSEvent *)event
242
+ {
243
+ RNGestureHandlerEventExtraData *extraData = [self extraDataForEvent:event];
244
+ [self sendEventsInState:RNGestureHandlerStateEnd forViewWithTag:_view.reactTag withExtraData:extraData];
245
+ [self sendEventsInState:RNGestureHandlerStateUndetermined forViewWithTag:_view.reactTag withExtraData:extraData];
248
246
  }
249
247
 
250
248
  @end
@@ -225,6 +225,10 @@
225
225
 
226
226
  recognizer.minimumPressDuration = 0.5;
227
227
  recognizer.allowableMovement = 10;
228
+
229
+ #if !TARGET_OS_TV
230
+ recognizer.numberOfTouchesRequired = 1;
231
+ #endif
228
232
  }
229
233
 
230
234
  - (void)configure:(NSDictionary *)config
@@ -42,7 +42,9 @@
42
42
 
43
43
  @implementation RNBetterPanGestureRecognizer {
44
44
  __weak RNGestureHandler *_gestureHandler;
45
- #if !TARGET_OS_OSX
45
+ #if TARGET_OS_OSX
46
+ BOOL _blockAutomaticActivation;
47
+ #else
46
48
  NSUInteger _realMinimumNumberOfTouches;
47
49
  #endif
48
50
  BOOL _hasCustomActivationCriteria;
@@ -114,6 +116,9 @@
114
116
 
115
117
  - (void)activateAfterLongPress
116
118
  {
119
+ #if TARGET_OS_OSX
120
+ _blockAutomaticActivation = NO;
121
+ #endif
117
122
  self.state = UIGestureRecognizerStateBegan;
118
123
  // Send event in ACTIVE state because UIGestureRecognizerStateBegan is mapped to RNGestureHandlerStateBegan
119
124
  [_gestureHandler handleGesture:self inState:RNGestureHandlerStateActive];
@@ -139,6 +144,11 @@
139
144
  #endif
140
145
 
141
146
  #if TARGET_OS_OSX
147
+ // NSPanGestureRecognizer transitions to the Began state on mouse-down, without any
148
+ // built-in movement hysteresis. To honor the custom activation criteria (minDist,
149
+ // activeOffsets, minVelocity) we hold the recognizer in the Possible state until
150
+ // they are met in interactionsMoved.
151
+ _blockAutomaticActivation = _hasCustomActivationCriteria;
142
152
  [super mouseDown:event];
143
153
  #else
144
154
  [super touchesBegan:touches withEvent:event];
@@ -176,7 +186,14 @@
176
186
  }
177
187
  }
178
188
 
179
- #if !TARGET_OS_TV && !TARGET_OS_OSX
189
+ #if TARGET_OS_OSX
190
+ if (_hasCustomActivationCriteria && self.state == UIGestureRecognizerStatePossible &&
191
+ [self shouldActivateUnderCustomCriteria]) {
192
+ _blockAutomaticActivation = NO;
193
+ self.state = UIGestureRecognizerStateBegan;
194
+ [self setTranslation:CGPointMake(0, 0) inView:self.view];
195
+ }
196
+ #elif !TARGET_OS_TV
180
197
  if (_hasCustomActivationCriteria && self.state == UIGestureRecognizerStatePossible &&
181
198
  [self shouldActivateUnderCustomCriteria]) {
182
199
  super.minimumNumberOfTouches = _realMinimumNumberOfTouches;
@@ -209,6 +226,23 @@
209
226
 
210
227
  #if TARGET_OS_OSX
211
228
 
229
+ - (void)setState:(NSGestureRecognizerState)state
230
+ {
231
+ if (_blockAutomaticActivation) {
232
+ if (state == NSGestureRecognizerStateBegan || state == NSGestureRecognizerStateChanged) {
233
+ // Hold the recognizer in the Possible state until the custom activation criteria
234
+ // are met — the superclass keeps tracking the mouse regardless, so translation
235
+ // and velocity stay valid for the criteria checks.
236
+ return;
237
+ }
238
+ if (state == NSGestureRecognizerStateEnded) {
239
+ // Mouse released before the activation criteria were met — the gesture failed.
240
+ state = NSGestureRecognizerStateFailed;
241
+ }
242
+ }
243
+ [super setState:state];
244
+ }
245
+
212
246
  - (void)mouseDown:(NSEvent *)event
213
247
  {
214
248
  [_gestureHandler setCurrentPointerTypeToMouse];
@@ -265,6 +299,9 @@
265
299
  [_gestureHandler.pointerTracker reset];
266
300
  [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(activateAfterLongPress) object:nil];
267
301
  self.enabled = YES;
302
+ #if TARGET_OS_OSX
303
+ _blockAutomaticActivation = NO;
304
+ #endif
268
305
  [super reset];
269
306
  [_gestureHandler reset];
270
307
 
@@ -333,10 +370,10 @@
333
370
  }
334
371
 
335
372
  CGPoint velocity = [self velocityInView:self.view];
336
- if (TEST_MIN_IF_NOT_NAN(velocity.x, _minVelocityX)) {
373
+ if (TEST_ABS_MIN_IF_NOT_NAN(velocity.x, _minVelocityX)) {
337
374
  return YES;
338
375
  }
339
- if (TEST_MIN_IF_NOT_NAN(velocity.y, _minVelocityY)) {
376
+ if (TEST_ABS_MIN_IF_NOT_NAN(velocity.y, _minVelocityY)) {
340
377
  return YES;
341
378
  }
342
379
  if (TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ(velocity), _minVelocitySq)) {
@@ -405,9 +442,9 @@
405
442
 
406
443
  #if !TARGET_OS_OSX && !TARGET_OS_TV && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400
407
444
  if (@available(iOS 13.4, *)) {
408
- bool enableTrackpadTwoFingerGesture = [RCTConvert BOOL:config[@"enableTrackpadTwoFingerGesture"]];
409
- if (enableTrackpadTwoFingerGesture) {
410
- recognizer.allowedScrollTypesMask = UIScrollTypeMaskAll;
445
+ id enableTrackpadTwoFingerGesture = config[@"enableTrackpadTwoFingerGesture"];
446
+ if (enableTrackpadTwoFingerGesture != nil) {
447
+ recognizer.allowedScrollTypesMask = [RCTConvert BOOL:enableTrackpadTwoFingerGesture] ? UIScrollTypeMaskAll : 0;
411
448
  }
412
449
  }
413
450
 
@@ -38,6 +38,12 @@
38
38
  NSUInteger _tapsSoFar;
39
39
  CGPoint _initPosition;
40
40
  NSInteger _maxNumberOfTouches;
41
+ // Pending `cancel` invocations scheduled via dispatch_after. We use dispatch blocks instead of
42
+ // performSelector:afterDelay: because the latter schedules its timer in NSDefaultRunLoopMode only,
43
+ // which means it is starved while a sibling UIScrollView keeps the run loop in UITrackingRunLoopMode
44
+ // (during a drag or momentum deceleration). dispatch_after fires regardless of run loop mode, so the
45
+ // tap can still fail/finalize on time while a list is scrolling. See issue #3471.
46
+ NSMutableArray<dispatch_block_t> *_pendingCancellations;
41
47
  }
42
48
 
43
49
  static const NSUInteger defaultNumberOfTaps = 1;
@@ -57,10 +63,33 @@ static const NSTimeInterval defaultMaxDuration = 0.5;
57
63
  _maxDeltaX = NAN;
58
64
  _maxDeltaY = NAN;
59
65
  _maxDistSq = NAN;
66
+ _pendingCancellations = [NSMutableArray array];
60
67
  }
61
68
  return self;
62
69
  }
63
70
 
71
+ - (void)scheduleCancelAfterDelay:(NSTimeInterval)delay
72
+ {
73
+ __weak typeof(self) weakSelf = self;
74
+
75
+ dispatch_block_t block = dispatch_block_create(0, ^{
76
+ [weakSelf cancel];
77
+ });
78
+
79
+ [_pendingCancellations addObject:block];
80
+
81
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
82
+ }
83
+
84
+ - (void)cancelPendingCancellations
85
+ {
86
+ for (dispatch_block_t block in _pendingCancellations) {
87
+ dispatch_block_cancel(block);
88
+ }
89
+
90
+ [_pendingCancellations removeAllObjects];
91
+ }
92
+
64
93
  - (void)triggerAction
65
94
  {
66
95
  [_gestureHandler handleGesture:self fromReset:NO];
@@ -93,14 +122,14 @@ static const NSTimeInterval defaultMaxDuration = 0.5;
93
122
  }
94
123
  _tapsSoFar++;
95
124
  if (_tapsSoFar) {
96
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
125
+ [self cancelPendingCancellations];
97
126
  }
98
127
  NSInteger numberOfTouches = [touches count];
99
128
  if (numberOfTouches > _maxNumberOfTouches) {
100
129
  _maxNumberOfTouches = numberOfTouches;
101
130
  }
102
131
  if (!isnan(_maxDuration)) {
103
- [self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDuration];
132
+ [self scheduleCancelAfterDelay:_maxDuration];
104
133
  }
105
134
  self.state = UIGestureRecognizerStatePossible;
106
135
  [self triggerAction];
@@ -132,10 +161,12 @@ static const NSTimeInterval defaultMaxDuration = 0.5;
132
161
  {
133
162
  [_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
134
163
 
164
+ [self cancelPendingCancellations];
165
+
135
166
  if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) {
136
167
  self.state = UIGestureRecognizerStateEnded;
137
168
  } else {
138
- [self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDelay];
169
+ [self scheduleCancelAfterDelay:_maxDelay];
139
170
  }
140
171
  }
141
172
 
@@ -250,7 +281,7 @@ static const NSTimeInterval defaultMaxDuration = 0.5;
250
281
 
251
282
  [_gestureHandler.pointerTracker reset];
252
283
 
253
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
284
+ [self cancelPendingCancellations];
254
285
  _tapsSoFar = 0;
255
286
  _maxNumberOfTouches = 0;
256
287
  self.enabled = YES;
package/apple/RNGHUIKit.h CHANGED
@@ -6,11 +6,11 @@ typedef UIView RNGHUIView;
6
6
  typedef UITouch RNGHUITouch;
7
7
  typedef UIScrollView RNGHUIScrollView;
8
8
 
9
- #define RNGHGestureRecognizerStateFailed UIGestureRecognizerStateFailed;
10
- #define RNGHGestureRecognizerStatePossible UIGestureRecognizerStatePossible;
11
- #define RNGHGestureRecognizerStateCancelled UIGestureRecognizerStateCancelled;
12
- #define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan;
13
- #define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded;
9
+ #define RNGHGestureRecognizerStateFailed UIGestureRecognizerStateFailed
10
+ #define RNGHGestureRecognizerStatePossible UIGestureRecognizerStatePossible
11
+ #define RNGHGestureRecognizerStateCancelled UIGestureRecognizerStateCancelled
12
+ #define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan
13
+ #define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded
14
14
 
15
15
  #else // TARGET_OS_OSX [
16
16
 
@@ -20,10 +20,10 @@ typedef RCTUIView RNGHUIView;
20
20
  typedef RCTUITouch RNGHUITouch;
21
21
  typedef NSScrollView RNGHUIScrollView;
22
22
 
23
- #define RNGHGestureRecognizerStateFailed NSGestureRecognizerStateFailed;
24
- #define RNGHGestureRecognizerStatePossible NSGestureRecognizerStatePossible;
25
- #define RNGHGestureRecognizerStateCancelled NSGestureRecognizerStateCancelled;
26
- #define RNGHGestureRecognizerStateBegan NSGestureRecognizerStateBegan;
27
- #define RNGHGestureRecognizerStateEnded NSGestureRecognizerStateEnded;
23
+ #define RNGHGestureRecognizerStateFailed NSGestureRecognizerStateFailed
24
+ #define RNGHGestureRecognizerStatePossible NSGestureRecognizerStatePossible
25
+ #define RNGHGestureRecognizerStateCancelled NSGestureRecognizerStateCancelled
26
+ #define RNGHGestureRecognizerStateBegan NSGestureRecognizerStateBegan
27
+ #define RNGHGestureRecognizerStateEnded NSGestureRecognizerStateEnded
28
28
 
29
29
  #endif // ] TARGET_OS_OSX
@@ -14,6 +14,8 @@
14
14
  #define TEST_MIN_IF_NOT_NAN(value, limit) \
15
15
  (!isnan(limit) && ((limit < 0 && value <= limit) || (limit >= 0 && value >= limit)))
16
16
 
17
+ #define TEST_ABS_MIN_IF_NOT_NAN(value, limit) (!isnan(limit) && fabs(value) >= fabs(limit))
18
+
17
19
  #define TEST_MAX_IF_NOT_NAN(value, max) (!isnan(max) && ((max < 0 && value < max) || (max >= 0 && value > max)))
18
20
 
19
21
  #define APPLY_PROP(recognizer, config, type, prop, propName) \
@@ -253,6 +253,24 @@ static NSHashTable<RNGestureHandler *> *allGestureHandlers;
253
253
 
254
254
  - (void)unbindFromView
255
255
  {
256
+ // If the gesture is still in flight - e.g. the view is being unmounted mid-gesture - deliver
257
+ // the final event now, while the recognizer is still attached and the target view is known.
258
+ // Otherwise the onBegin/onFinalize and onActivate/onDeactivate guarantees would be broken
259
+ // and `_lastState` would never be cleared by `reset`.
260
+ if (self.recognizer.view != nil &&
261
+ (_lastState == RNGestureHandlerStateBegan || _lastState == RNGestureHandlerStateActive)) {
262
+ if ([self eventTagForRecognizer:self.recognizer] != nil) {
263
+ [self handleGesture:self.recognizer
264
+ inState:_lastState == RNGestureHandlerStateActive ? RNGestureHandlerStateCancelled
265
+ : RNGestureHandlerStateFailed];
266
+ } else {
267
+ // The event has no tag to be dispatched with, so it cannot be delivered on any path - reset
268
+ // the bookkeeping so the handler doesn't stay in-flight forever.
269
+ _lastState = RNGestureHandlerStateUndetermined;
270
+ _state = RNGestureHandlerStateBegan;
271
+ }
272
+ }
273
+
256
274
  [self.recognizer.view removeGestureRecognizer:self.recognizer];
257
275
  self.recognizer.delegate = nil;
258
276
  self.viewTag = nil;
@@ -314,21 +332,38 @@ static NSHashTable<RNGestureHandler *> *allGestureHandlers;
314
332
  //
315
333
  // While this solution is not great, we decided to check whether sending events was triggered from `reset` method.
316
334
  // This way we can detect double Began mapping by checking previous sent state and current state of recognizer.
317
- if (fromReset && _lastState == RNGestureHandlerStateBegan &&
318
- self.recognizer.state == UIGestureRecognizerStatePossible) {
319
- _state = RNGestureHandlerStateFailed;
335
+ //
336
+ // The same applies to gestures interrupted mid-flight, e.g. when the view is unmounted during an active
337
+ // gesture the recognizer may be reset without its cancel action ever firing.
338
+ // If the last sent state is not final, synthesize the missing final event so that the
339
+ // `onBegin`/`onFinalize` and `onActivate`/`onDeactivate` guarantees hold.
340
+ if (fromReset && self.recognizer.state == UIGestureRecognizerStatePossible) {
341
+ if (_lastState == RNGestureHandlerStateBegan) {
342
+ _state = RNGestureHandlerStateFailed;
343
+ } else if (_lastState == RNGestureHandlerStateActive) {
344
+ _state = RNGestureHandlerStateCancelled;
345
+ } else {
346
+ // The final event was already delivered; mapping Possible to Began here would emit a stray
347
+ // BEGAN event after the gesture has finished.
348
+ return;
349
+ }
320
350
  }
321
351
 
322
352
  [self handleGesture:recognizer inState:_state];
323
353
  }
324
354
 
355
+ - (nullable NSNumber *)eventTagForRecognizer:(UIGestureRecognizer *)recognizer
356
+ {
357
+ return [self chooseViewForInteraction:recognizer].reactTag;
358
+ }
359
+
325
360
  - (void)handleGesture:(UIGestureRecognizer *)recognizer inState:(RNGestureHandlerState)state
326
361
  {
327
362
  _state = state;
328
363
  RNGestureHandlerEventExtraData *eventData = [self eventExtraData:recognizer];
329
- RNGHUIView *view = [self chooseViewForInteraction:recognizer];
364
+ NSNumber *tag = [self eventTagForRecognizer:recognizer];
330
365
 
331
- [self sendEventsInState:self.state forViewWithTag:view.reactTag withExtraData:eventData];
366
+ [self sendEventsInState:self.state forViewWithTag:tag withExtraData:eventData];
332
367
  }
333
368
 
334
369
  - (void)sendEventsInState:(RNGestureHandlerState)state
@@ -636,7 +671,13 @@ static NSHashTable<RNGestureHandler *> *allGestureHandlers;
636
671
  // might be called after some pointers are down, and after state manipulation by the user.
637
672
  // Pointer tracker calls this method when it resets, and in that case it no longer tracks
638
673
  // any pointers, thus entering this if
639
- if (!_needsPointerData || _pointerTracker.trackedPointersCount == 0) {
674
+ //
675
+ // Also do not clear _lastState while the gesture is in flight (BEGAN/ACTIVE) - the final
676
+ // state-change event hasn't been dispatched yet. When the view is removed mid-gesture,
677
+ // the pointer tracker resets before the recognizer's cancel action fires; clearing _lastState
678
+ // here would corrupt the prevState of the outgoing CANCELLED event and break the onActivate/onDeactivate guarantee.
679
+ if ((!_needsPointerData || _pointerTracker.trackedPointersCount == 0) && _lastState != RNGestureHandlerStateBegan &&
680
+ _lastState != RNGestureHandlerStateActive) {
640
681
  _lastState = RNGestureHandlerStateUndetermined;
641
682
  _state = RNGestureHandlerStateBegan;
642
683
  }
@@ -62,7 +62,8 @@
62
62
  {
63
63
  if ([view isKindOfClass:[RNGestureHandlerButton class]]) {
64
64
  RNGestureHandlerButton *button = (RNGestureHandlerButton *)view;
65
- return button.userEnabled;
65
+ return button.userEnabled && button.pointerEvents != RNGestureHandlerPointerEventsBoxNone &&
66
+ button.pointerEvents != RNGestureHandlerPointerEventsNone;
66
67
  }
67
68
 
68
69
  // Certain subviews such as RCTViewComponentView have been observed to have disabled
@@ -105,7 +106,8 @@
105
106
  if (!subview.isHidden && subview.alpha > 0) {
106
107
  CGPoint convertedPoint = [subview convertPoint:point fromView:self];
107
108
  UIView *hitView = [subview hitTest:convertedPoint withEvent:event];
108
- if (hitView != nil && [self shouldHandleTouch:hitView]) {
109
+ if (hitView != nil &&
110
+ ([hitView isKindOfClass:[RNGestureHandlerButton class]] || [self shouldHandleTouch:hitView])) {
109
111
  return hitView;
110
112
  }
111
113
  }
@@ -118,7 +120,7 @@
118
120
  }
119
121
 
120
122
  RNGHUIView *inner = [super hitTest:point withEvent:event];
121
- while (inner && ![self shouldHandleTouch:inner]) {
123
+ while (inner && inner != self && ![self shouldHandleTouch:inner]) {
122
124
  inner = inner.superview;
123
125
  }
124
126
  return inner;
@@ -4,7 +4,6 @@
4
4
  #import <React/RCTEventDispatcherProtocol.h>
5
5
  #import <React/RCTLog.h>
6
6
  #import <React/RCTModalHostViewController.h>
7
- #import <React/RCTRootContentView.h>
8
7
  #import <React/RCTRootView.h>
9
8
  #import <React/RCTUIManager.h>
10
9
  #import <React/RCTViewManager.h>
@@ -275,20 +274,50 @@ constexpr int NEW_ARCH_NUMBER_OF_ATTACH_RETRIES = 25;
275
274
 
276
275
  #pragma mark Root Views Management
277
276
 
277
+ #ifdef RCT_NEW_ARCH_ENABLED
278
+ #if !TARGET_OS_OSX
279
+ static BOOL RNGHIsScreensTouchHandlerHost(RNGHUIView *view)
280
+ {
281
+ static Class fullWindowOverlayContainerClass;
282
+ static Class screenViewClass;
283
+ static SEL isModalSelector;
284
+ static dispatch_once_t onceToken;
285
+ dispatch_once(&onceToken, ^{
286
+ fullWindowOverlayContainerClass = NSClassFromString(@"RNSFullWindowOverlayContainer");
287
+ screenViewClass = NSClassFromString(@"RNSScreenView");
288
+ isModalSelector = NSSelectorFromString(@"isModal");
289
+ });
290
+
291
+ if (fullWindowOverlayContainerClass != nil && [view isKindOfClass:fullWindowOverlayContainerClass]) {
292
+ return YES;
293
+ }
294
+
295
+ if (screenViewClass != nil && [view isKindOfClass:screenViewClass]) {
296
+ // For now we consider only modals
297
+ return [view respondsToSelector:isModalSelector] && [[view valueForKey:@"isModal"] boolValue];
298
+ }
299
+
300
+ return NO;
301
+ }
302
+ #endif // !TARGET_OS_OSX
303
+ #endif // RCT_NEW_ARCH_ENABLED
304
+
278
305
  - (void)registerViewWithGestureRecognizerAttachedIfNeeded:(RNGHUIView *)childView
306
+ {
307
+ [self registerViewWithGestureRecognizerAttachedIfNeeded:childView isRetry:NO];
308
+ }
309
+
310
+ - (void)registerViewWithGestureRecognizerAttachedIfNeeded:(RNGHUIView *)childView isRetry:(BOOL)isRetry
279
311
  {
280
312
  #ifdef RCT_NEW_ARCH_ENABLED
281
313
  RNGHUIView *touchHandlerView = childView;
282
314
 
283
315
  #if !TARGET_OS_OSX
284
- Class fullWindowOverlayContainerClass = NSClassFromString(@"RNSFullWindowOverlayContainer");
285
-
286
316
  if ([[childView reactViewController] isKindOfClass:[RCTFabricModalHostViewController class]]) {
287
317
  touchHandlerView = [childView reactViewController].view;
288
318
  } else {
289
- while (
290
- touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]] &&
291
- (fullWindowOverlayContainerClass == nil || ![touchHandlerView isKindOfClass:fullWindowOverlayContainerClass])) {
319
+ while (touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]] &&
320
+ !RNGHIsScreensTouchHandlerHost(touchHandlerView)) {
292
321
  touchHandlerView = touchHandlerView.superview;
293
322
  }
294
323
  }
@@ -324,6 +353,19 @@ constexpr int NEW_ARCH_NUMBER_OF_ATTACH_RETRIES = 25;
324
353
  #endif // RCT_NEW_ARCH_ENABLED
325
354
 
326
355
  if (touchHandlerView == nil) {
356
+ #ifdef RCT_NEW_ARCH_ENABLED
357
+ #if !TARGET_OS_OSX
358
+ // Handlers are attached while the mounting transaction is still in progress — the view's
359
+ // ancestor chain may not be assembled yet (Fabric connects subtrees children-first), in
360
+ // which case the walk above dead-ends before reaching any touch-handling root. Retry once
361
+ // on the next run loop turn, when mounting has finished and the hierarchy is connected.
362
+ if (!isRetry) {
363
+ dispatch_async(dispatch_get_main_queue(), ^{
364
+ [self registerViewWithGestureRecognizerAttachedIfNeeded:childView isRetry:YES];
365
+ });
366
+ }
367
+ #endif // !TARGET_OS_OSX
368
+ #endif // RCT_NEW_ARCH_ENABLED
327
369
  return;
328
370
  }
329
371
 
@@ -382,6 +424,21 @@ constexpr int NEW_ARCH_NUMBER_OF_ATTACH_RETRIES = 25;
382
424
  break;
383
425
  }
384
426
  }
427
+
428
+ #ifdef RCT_NEW_ARCH_ENABLED
429
+ #if !TARGET_OS_OSX
430
+ if (touchHandler == nil && [viewWithTouchHandler respondsToSelector:NSSelectorFromString(@"touchHandler")]) {
431
+ // An RNSScreenView may not have the touch handler attached directly (e.g. touches on it are
432
+ // still driven by an ancestor's touch handler) — ask react-native-screens for the one
433
+ // responsible for this screen.
434
+ id screenTouchHandler = [viewWithTouchHandler valueForKey:@"touchHandler"];
435
+ if ([screenTouchHandler isKindOfClass:[RCTSurfaceTouchHandler class]]) {
436
+ touchHandler = screenTouchHandler;
437
+ }
438
+ }
439
+ #endif // !TARGET_OS_OSX
440
+ #endif // RCT_NEW_ARCH_ENABLED
441
+
385
442
  [touchHandler setEnabled:NO];
386
443
  [touchHandler setEnabled:YES];
387
444
  }