@@ -39,6 +39,8 @@ @implementation RCTTouchHandler
39
39
NSMutableArray <NSMutableDictionary *> *_reactTouches;
40
40
NSMutableArray <UIView *> *_touchViews;
41
41
42
+ __weak UIView *_cachedRootView;
43
+
42
44
uint16_t _coalescingKey;
43
45
}
44
46
@@ -150,7 +152,8 @@ - (void)_updateReactTouchAtIndex:(NSInteger)touchIndex
150
152
{
151
153
UITouch *nativeTouch = _nativeTouches[touchIndex];
152
154
CGPoint windowLocation = [nativeTouch locationInView: nativeTouch.window];
153
- CGPoint rootViewLocation = [nativeTouch.window convertPoint: windowLocation toView: self .view];
155
+ RCTAssert (_cachedRootView, @" We were unable to find a root view for the touch" );
156
+ CGPoint rootViewLocation = [nativeTouch.window convertPoint: windowLocation toView: _cachedRootView];
154
157
155
158
UIView *touchView = _touchViews[touchIndex];
156
159
CGPoint touchViewLocation = [nativeTouch.window convertPoint: windowLocation toView: touchView];
@@ -231,6 +234,26 @@ - (void)_updateAndDispatchTouches:(NSSet<UITouch *> *)touches
231
234
[_eventDispatcher sendEvent: event];
232
235
}
233
236
237
+ /* **
238
+ * To ensure compatibilty when using UIManager.measure and RCTTouchHandler, we have to adopt
239
+ * UIManager.measure's behavior in finding a "root view".
240
+ * Usually RCTTouchHandler is already attached to a root view but in some cases (e.g. Modal),
241
+ * we are instead attached to some RCTView subtree. This is also the case when embedding some RN
242
+ * views inside a seperate ViewController not controlled by RN.
243
+ * This logic will either find the nearest rootView, or go all the way to the UIWindow.
244
+ * While this is not optimal, it is exactly what UIManager.measure does, and what Touchable.js
245
+ * relies on.
246
+ * We cache it here so that we don't have to repeat it for every touch in the gesture.
247
+ */
248
+ - (void )_cacheRootView
249
+ {
250
+ UIView *rootView = self.view ;
251
+ while (rootView.superview && ![rootView isReactRootView ]) {
252
+ rootView = rootView.superview ;
253
+ }
254
+ _cachedRootView = rootView;
255
+ }
256
+
234
257
#pragma mark - Gesture Recognizer Delegate Callbacks
235
258
236
259
static BOOL RCTAllTouchesAreCancelledOrEnded (NSSet <UITouch *> *touches)
@@ -262,6 +285,8 @@ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
262
285
{
263
286
[super touchesBegan: touches withEvent: event];
264
287
288
+ [self _cacheRootView ];
289
+
265
290
// "start" has to record new touches *before* extracting the event.
266
291
// "end"/"cancel" needs to remove the touch *after* extracting the event.
267
292
[self _recordNewTouches: touches];
@@ -333,6 +358,8 @@ - (void)reset
333
358
[_nativeTouches removeAllObjects ];
334
359
[_reactTouches removeAllObjects ];
335
360
[_touchViews removeAllObjects ];
361
+
362
+ _cachedRootView = nil ;
336
363
}
337
364
}
338
365
0 commit comments