Measure touch events from nearest "root view"
Summary: This makes RCTTouchHandler follow the same logic when sending touch event coordinates as `UIManager.measure`. This is important as UIManager.measure is used in `Touchable.js` aka the mother of all touch events in RN. In particular, this will make touch events correctly handled for places where RN is embedded into other screens. Reviewed By: shergin Differential Revision: D6838102 fbshipit-source-id: 70cad52606ea931cb637d8aa2d4845818eea0647
This commit is contained in:
parent
b1cdb7d553
commit
a70fdac5bd
|
@ -39,6 +39,8 @@
|
|||
NSMutableArray<NSMutableDictionary *> *_reactTouches;
|
||||
NSMutableArray<UIView *> *_touchViews;
|
||||
|
||||
__weak UIView *_cachedRootView;
|
||||
|
||||
uint16_t _coalescingKey;
|
||||
}
|
||||
|
||||
|
@ -150,7 +152,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action
|
|||
{
|
||||
UITouch *nativeTouch = _nativeTouches[touchIndex];
|
||||
CGPoint windowLocation = [nativeTouch locationInView:nativeTouch.window];
|
||||
CGPoint rootViewLocation = [nativeTouch.window convertPoint:windowLocation toView:self.view];
|
||||
RCTAssert(_cachedRootView, @"We were unable to find a root view for the touch");
|
||||
CGPoint rootViewLocation = [nativeTouch.window convertPoint:windowLocation toView:_cachedRootView];
|
||||
|
||||
UIView *touchView = _touchViews[touchIndex];
|
||||
CGPoint touchViewLocation = [nativeTouch.window convertPoint:windowLocation toView:touchView];
|
||||
|
@ -231,6 +234,26 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action
|
|||
[_eventDispatcher sendEvent:event];
|
||||
}
|
||||
|
||||
/***
|
||||
* To ensure compatibilty when using UIManager.measure and RCTTouchHandler, we have to adopt
|
||||
* UIManager.measure's behavior in finding a "root view".
|
||||
* Usually RCTTouchHandler is already attached to a root view but in some cases (e.g. Modal),
|
||||
* we are instead attached to some RCTView subtree. This is also the case when embedding some RN
|
||||
* views inside a seperate ViewController not controlled by RN.
|
||||
* This logic will either find the nearest rootView, or go all the way to the UIWindow.
|
||||
* While this is not optimal, it is exactly what UIManager.measure does, and what Touchable.js
|
||||
* relies on.
|
||||
* We cache it here so that we don't have to repeat it for every touch in the gesture.
|
||||
*/
|
||||
- (void)_cacheRootView
|
||||
{
|
||||
UIView *rootView = self.view;
|
||||
while (rootView.superview && ![rootView isReactRootView]) {
|
||||
rootView = rootView.superview;
|
||||
}
|
||||
_cachedRootView = rootView;
|
||||
}
|
||||
|
||||
#pragma mark - Gesture Recognizer Delegate Callbacks
|
||||
|
||||
static BOOL RCTAllTouchesAreCancelledOrEnded(NSSet<UITouch *> *touches)
|
||||
|
@ -262,6 +285,8 @@ static BOOL RCTAnyTouchesChanged(NSSet<UITouch *> *touches)
|
|||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
|
||||
[self _cacheRootView];
|
||||
|
||||
// "start" has to record new touches *before* extracting the event.
|
||||
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
||||
[self _recordNewTouches:touches];
|
||||
|
@ -333,6 +358,8 @@ static BOOL RCTAnyTouchesChanged(NSSet<UITouch *> *touches)
|
|||
[_nativeTouches removeAllObjects];
|
||||
[_reactTouches removeAllObjects];
|
||||
[_touchViews removeAllObjects];
|
||||
|
||||
_cachedRootView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue