Skip to content

[iOS] Fix InterceptingGestureDetector not recognizing gestures #4170

Merged
m-bert merged 9 commits into
mainfrom
@mbert/intercepting-detector-hierarchy
May 15, 2026
Merged

[iOS] Fix InterceptingGestureDetector not recognizing gestures #4170
m-bert merged 9 commits into
mainfrom
@mbert/intercepting-detector-hierarchy

Conversation

@m-bert
Copy link
Copy Markdown
Collaborator

@m-bert m-bert commented May 12, 2026

Description

This PR fixes VirtualGestureDetector with InterceptingGestureDetector above the content view. Virtual gestures work correctly in the following configuration:

<View style={styles.messageContainer}>
  <InterceptingGestureDetector>
    <Text>
      <VirtualGestureDetector gesture={tapGesture}>

but they don't work in this one:

<InterceptingGestureDetector>
  <View style={styles.messageContainer}>
    <Text>
      <VirtualGestureDetector gesture={tapGesture}>

iOS

Added a generic recursive helper isVirtualViewTag:touchedAtPoint:inView: that walks the view subtree at a given point and uses touchEventEmitterAtPoint: (from RCTTouchableComponentViewProtocol) to check whether the virtual tag was hit. This replaces the previous RCTParagraphComponentView-specific check, making the mechanism work regardless of which view type contains the virtual content.

Android

Note

Seems that Android changes were unnecessary and were removed in 094cd23.

RNGestureHandlerDetectorView now implements ReactCompoundView and overrides reactTagForTouch. The orchestrator calls reactTagForTouch on any ReactCompoundView it encounters during traversal and looks up handlers registered for the returned tag. Previously, when InterceptingGestureDetector sat between the outer GestureDetector and the row containing VirtualGestureDetector children, the orchestrator never discovered the virtual handlers.

Test plan

Tested on the following code
import React from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import {
  GestureDetector,
  InterceptingGestureDetector,
  useLongPressGesture,
  useTapGesture,
  VirtualGestureDetector,
} from 'react-native-gesture-handler';

const BufferLine: React.FC<any> = ({ line, onLongPress, nickWidth }) => {
  const longPressAll = useLongPressGesture({
    onActivate: () => {
      onLongPress(line);
    },
    runOnJS: true,
  });

  const tapGesture = useTapGesture({
    onActivate: () => console.log('tap on nick:', line.nick),
    runOnJS: true,
  });

  return (
    <GestureDetector gesture={longPressAll}>
      <InterceptingGestureDetector>
        <View style={styles.messageContainer}>
          <Text>
            <VirtualGestureDetector gesture={tapGesture}>
              <Text style={{ width: nickWidth, fontWeight: 'bold' }}>
                {line.nick}
              </Text>
            </VirtualGestureDetector>
            {'  '}
            {line.message}
          </Text>
        </View>
      </InterceptingGestureDetector>
    </GestureDetector>
  );
};

const SAMPLE_LINES = [
  { nick: 'alice', message: 'Hello everyone!' },
  { nick: 'bob', message: 'Hey Alice!' },
  { nick: 'charlie', message: 'What are you all up to?' },
  { nick: 'alice', message: 'Just testing this gesture handler repro' },
  { nick: 'dave', message: 'Looks good to me!' },
  { nick: 'eve', message: 'Can you scroll through this list?' },
  { nick: 'bob', message: 'Long press any row to trigger the callback' },
  { nick: 'charlie', message: 'Tap a nick to trigger the tap gesture' },
  { nick: 'alice', message: 'Both should work without blocking each other' },
  { nick: 'dave', message: 'And scrolling should still work fine' },
];

export default function EmptyExample() {
  return (
    <ScrollView style={styles.container}>
      {SAMPLE_LINES.map((line, i) => (
        <BufferLine
          key={i}
          line={line}
          onLongPress={(l: any) => console.log('long press on line:', l.nick)}
          nickWidth={70}
        />
      ))}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  messageContainer: {
    paddingVertical: 6,
    paddingHorizontal: 12,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: '#ccc',
  },
});

Copilot AI review requested due to automatic review settings May 12, 2026 15:02
@m-bert m-bert changed the title [Android | iOS] [Android | iOS] Fix InterceptingGestureDetector not recognizing gestures May 12, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the native (iOS + Android) “virtual detector” hit-testing logic so VirtualGestureDetector can correctly decide whether the current touch should be attributed to a specific virtual view tag (e.g., inline/nested text targets) within the detector’s subtree.

Changes:

  • iOS: add a recursive subtree search that uses touchEventEmitterAtPoint: to detect whether a touch corresponds to a given virtualViewTag.
  • iOS: apply the new virtual-tag hit-testing in containsPointInView, wantsToHandleEventsAtPoint:, and gestureRecognizerShouldBegin.
  • Android: make RNGestureHandlerDetectorView implement ReactCompoundView and add reactTagForTouch-based routing for virtual children.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/react-native-gesture-handler/apple/RNGestureHandler.mm Adds recursive emitter-tag-based hit-testing for virtual detectors and uses it to gate handling/begin conditions.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt Implements ReactCompoundView and adds touch→virtual-tag mapping logic for detector views.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread packages/react-native-gesture-handler/apple/RNGestureHandler.mm
m-bert and others added 2 commits May 13, 2026 09:01
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread packages/react-native-gesture-handler/apple/RNGestureHandler.mm Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@m-bert m-bert marked this pull request as ready for review May 13, 2026 07:39
@m-bert m-bert requested a review from j-piasecki May 13, 2026 07:39
@m-bert m-bert requested a review from coado May 15, 2026 09:56
@m-bert m-bert changed the title [Android | iOS] Fix InterceptingGestureDetector not recognizing gestures [iOS] Fix InterceptingGestureDetector not recognizing gestures May 15, 2026
@m-bert m-bert merged commit 870b963 into main May 15, 2026
3 checks passed
@m-bert m-bert deleted the @mbert/intercepting-detector-hierarchy branch May 15, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants