Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
inState:(RNGestureHandlerState)state
fromManualStateChange:(BOOL)fromManualStateChange;
- (BOOL)containsPointInView;
- (BOOL)wantsToHandleEventsAtPoint:(CGPoint)point;
- (RNGestureHandlerState)state;
- (nullable RNGestureHandlerEventExtraData *)eventExtraData:(nonnull id)recognizer;

Expand Down
27 changes: 27 additions & 0 deletions packages/react-native-gesture-handler/apple/RNGestureHandler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,33 @@ - (BOOL)containsPointInView
return CGRectContainsPoint(hitFrame, location);
}

- (BOOL)wantsToHandleEventsAtPoint:(CGPoint)point
{
RNGHUIView *viewToHitTest = _recognizer.view;

if ([self usesNativeOrVirtualDetector] && [_recognizer.view.subviews count] > 0) {
viewToHitTest = _recognizer.view.subviews[0];
point = [_recognizer.view convertPoint:point toView:viewToHitTest];
}

if (_actionType == RNGestureHandlerActionTypeVirtualDetector && _virtualViewTag != nil) {
// In this case, logic detector is attached to the DetectorView, which has a single subview representing
// the actual target view in the RN hierarchy
if ([viewToHitTest respondsToSelector:@selector(touchEventEmitterAtPoint:)]) {
// If the view has touchEventEmitterAtPoint: method, it can be used to determine the viewtag
// of the view under the touch point
facebook::react::SharedTouchEventEmitter eventEmitter =
[(id<RCTTouchableComponentViewProtocol>)viewToHitTest touchEventEmitterAtPoint:point];
auto viewUnderTouch = eventEmitter->getEventTarget()->getTag();

return viewUnderTouch == [_virtualViewTag intValue];
}
}

CGRect hitFrame = RNGHHitSlopInsetRect(viewToHitTest.bounds, _hitSlop);
return CGRectContainsPoint(hitFrame, point);
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([_handlersToWaitFor count]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,6 @@ - (void)layout
[self applyUnderlayCornerRadii];
}

- (BOOL)shouldHandleTouch:(RNGHUIView *)view
{
if ([view isKindOfClass:[RNGestureHandlerButton class]]) {
RNGestureHandlerButton *button = (RNGestureHandlerButton *)view;
return button.userEnabled;
}

// Certain subviews such as RCTViewComponentView have been observed to have disabled
// accessibility gesture recognizers such as _UIAccessibilityHUDGateGestureRecognizer,
// ostensibly set by iOS. Such gesture recognizers cause this function to return YES
// even when the passed view is static text and does not respond to touches. This in
// turn prevents the button from receiving touches, breaking functionality. To handle
// such case, we can count only the enabled gesture recognizers when determining
// whether a view should receive touches.
NSPredicate *isEnabledPredicate = [NSPredicate predicateWithFormat:@"isEnabled == YES"];
NSArray *enabledGestureRecognizers = [view.gestureRecognizers filteredArrayUsingPredicate:isEnabledPredicate];

#if !TARGET_OS_OSX
return [view isKindOfClass:[UIControl class]] || [enabledGestureRecognizers count] > 0;
#else
return [view isKindOfClass:[NSControl class]] || [enabledGestureRecognizers count] > 0;
#endif
}

- (void)animateUnderlayToOpacity:(float)toOpacity duration:(NSTimeInterval)durationMs
{
_underlayLayer.opacity =
Expand Down Expand Up @@ -667,6 +643,44 @@ - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
_isTouchInsideBounds = NO;
}

- (BOOL)shouldHandleTouch:(RNGHUIView *)view atPoint:(CGPoint)point
{
if ([view isKindOfClass:[RNGestureHandlerButton class]]) {
RNGestureHandlerButton *button = (RNGestureHandlerButton *)view;
return button.userEnabled;
}

// Certain subviews such as RCTViewComponentView have been observed to have disabled
// accessibility gesture recognizers such as _UIAccessibilityHUDGateGestureRecognizer,
// ostensibly set by iOS. Such gesture recognizers cause this function to return YES
// even when the passed view is static text and does not respond to touches. This in
// turn prevents the button from receiving touches, breaking functionality. To handle
// such case, we can count only the enabled gesture recognizers when determining
// whether a view should receive touches.
NSPredicate *isEnabledPredicate = [NSPredicate predicateWithFormat:@"isEnabled == YES"];
NSArray *enabledGestureRecognizers = [view.gestureRecognizers filteredArrayUsingPredicate:isEnabledPredicate];

BOOL gestureRecognizerWantsEvent = NO;
for (UIGestureRecognizer *recognizer in enabledGestureRecognizers) {
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:recognizer];
if (handler != nil) {
CGPoint pointInView = [self convertPoint:point toView:view];
gestureRecognizerWantsEvent = [handler wantsToHandleEventsAtPoint:pointInView];
} else {
gestureRecognizerWantsEvent = YES;
}
if (gestureRecognizerWantsEvent) {
break;
}
}

#if !TARGET_OS_OSX
return [view isKindOfClass:[UIControl class]] || gestureRecognizerWantsEvent;
#else
return [view isKindOfClass:[NSControl class]] || [enabledGestureRecognizers count] > 0;
#endif
}

- (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
RNGestureHandlerPointerEvents pointerEvents = _pointerEvents;
Expand All @@ -680,7 +694,7 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
if (!subview.isHidden && subview.alpha > 0) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitView = [subview hitTest:convertedPoint withEvent:event];
if (hitView != nil && [self shouldHandleTouch:hitView]) {
if (hitView != nil && [self shouldHandleTouch:hitView atPoint:point]) {
return hitView;
}
}
Expand All @@ -693,7 +707,7 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
}

RNGHUIView *inner = [super hitTest:point withEvent:event];
while (inner && ![self shouldHandleTouch:inner]) {
while (inner && ![self shouldHandleTouch:inner atPoint:point]) {
inner = inner.superview;
}
return inner;
Expand Down
Loading