Skip to content

Commit

Permalink
Correctly attach root view recognizer in modals on iOS (#2498)
Browse files Browse the repository at this point in the history
## Description

On iOS, the conditions to attach `RootViewGestureRecognizer` were
satisfied neither on the new nor the old architecture.
This PR changes this logic:
- unifies extracting the RN's touch handler to simply loop on all
recognizers attached to the root view, as it works in every case
- adds a check for `ModalHostViewController` when attaching the root
view recognizer and if the view is in the modal the logic is different:
- on the old arch:
[RCTModalHostView](https://github.com/facebook/react-native/blob/b50874cad41f06519a263fa53d15f76c836fd167/packages/react-native/React/Views/RCTModalHostView.m#L95-L100)
has a `UIView` as its child, and this one may only have one `RCTView` as
its child - this one has the `RCTTouchHandler` recognizer attached
- on the new arch:
[RCTFabricModalHostViewController](https://github.com/facebook/react-native/blob/b50874cad41f06519a263fa53d15f76c836fd167/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm#L42)
directly attaches `RCTSurfaceTouchHandler` to its direct subview

Fixes
#2487.

## Test plan

Tested on Example and FabricExample apps and on the code from the issue.

---------

Co-authored-by: Tomek Zawadzki <tomekzawadzki98@gmail.com>
  • Loading branch information
j-piasecki and tomekzaw authored Jun 7, 2023
1 parent 69b034a commit 4d5aae1
Showing 1 changed file with 24 additions and 12 deletions.
36 changes: 24 additions & 12 deletions ios/RNGestureHandlerManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#import <React/RCTComponent.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTLog.h>
#import <React/RCTModalHostViewController.h>
#import <React/RCTRootContentView.h>
#import <React/RCTRootView.h>
#import <React/RCTUIManager.h>
Expand All @@ -15,6 +16,7 @@
#import "RNRootViewGestureRecognizer.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTFabricModalHostViewController.h>
#import <React/RCTSurfaceTouchHandler.h>
#import <React/RCTSurfaceView.h>
#import <React/RCTViewComponentView.h>
Expand Down Expand Up @@ -199,15 +201,26 @@ - (void)registerViewWithGestureRecognizerAttachedIfNeeded:(UIView *)childView
#ifdef RCT_NEW_ARCH_ENABLED
UIView *touchHandlerView = childView;

while (touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]]) {
touchHandlerView = touchHandlerView.superview;
if ([[childView reactViewController] isKindOfClass:[RCTFabricModalHostViewController class]]) {
touchHandlerView = [childView reactViewController].view;
} else {
while (touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]]) {
touchHandlerView = touchHandlerView.superview;
}
}
#else
UIView *parent = childView;
while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)])
parent = parent.superview;
UIView *touchHandlerView = nil;

if ([[childView reactViewController] isKindOfClass:[RCTModalHostViewController class]]) {
touchHandlerView = [childView reactViewController].view.subviews[0];
} else {
UIView *parent = childView;
while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)]) {
parent = parent.superview;
}

UIView *touchHandlerView = [[parent performSelector:@selector(touchHandler)] view];
touchHandlerView = [[parent performSelector:@selector(touchHandler)] view];
}
#endif // RCT_NEW_ARCH_ENABLED

if (touchHandlerView == nil) {
Expand Down Expand Up @@ -247,20 +260,19 @@ - (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
if ([gestureRecognizer.view isKindOfClass:[UIScrollView class]])
return;

#ifdef RCT_NEW_ARCH_ENABLED
UIGestureRecognizer *touchHandler = nil;

// touchHandler (RCTSurfaceTouchHandler) is private in RCTFabricSurface so we have to do
// this little trick to get access to it
// this way we can extract the touch handler on both architectures relatively easily
for (UIGestureRecognizer *recognizer in [viewWithTouchHandler gestureRecognizers]) {
#ifdef RCT_NEW_ARCH_ENABLED
if ([recognizer isKindOfClass:[RCTSurfaceTouchHandler class]]) {
#else
if ([recognizer isKindOfClass:[RCTTouchHandler class]]) {
#endif // RCT_NEW_ARCH_ENABLED
touchHandler = recognizer;
break;
}
}
#else
RCTTouchHandler *touchHandler = [viewWithTouchHandler performSelector:@selector(touchHandler)];
#endif // RCT_NEW_ARCH_ENABLED
[touchHandler setEnabled:NO];
[touchHandler setEnabled:YES];
}
Expand Down

0 comments on commit 4d5aae1

Please sign in to comment.