Skip to content

Conversation

@j-piasecki
Copy link
Member

Description

I noticed that #3756 restored calling triggerAction to all gestures that were affected by #3740, but not to Pan. This is causing Pan to trigger onBegin without then triggering onFinalize.

This PR restores calling triggerAction in reset.

Test plan

import { View, ScrollView } from 'react-native';
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from 'react-native-gesture-handler';

export default function App() {
  const pan = Gesture.Pan()
    .runOnJS(true)
    .onBegin(() => {
      'worklet';
      console.log('begin');
    })
    .onFinalize(() => {
      'worklet';
      console.log('finalize');
    });
  return (
    <GestureHandlerRootView>
      <GestureDetector gesture={pan}>
        <View style={{ flex: 1, backgroundColor: 'blue' }}>
          <ScrollViewExample pan={pan} />
        </View>
      </GestureDetector>
    </GestureHandlerRootView>
  );
}

function ScrollViewExample({ pan }: any) {
  const native = Gesture.Native().blocksExternalGesture(pan).runOnJS(true);
  return (
    <GestureDetector gesture={native}>
      <ScrollView
        horizontal
        pagingEnabled
        style={{
          flex: 1,
          backgroundColor: 'yellow',
        }}>
        <View
          style={{
            width: 250,
            height: '100%',
            backgroundColor: 'green',
          }}
        />
        <View
          style={{
            width: 250,
            height: '100%',
            backgroundColor: 'purple',
          }}
        />
      </ScrollView>
    </GestureDetector>
  );
}

Copy link
Contributor

@m-bert m-bert left a comment

Choose a reason for hiding this comment

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

Seems like it doesn't work on iOS 26 😞

26

Nagranie.z.ekranu.2025-11-28.o.20.22.37.mov

18.5

Nagranie.z.ekranu.2025-11-28.o.20.23.42.mov

@j-piasecki
Copy link
Member Author

@m-bert It seems like the iOS 26 bug is unrelated to this change. Do you think this one can be merged, or do you prefer both to be fixed in one go?

Copy link
Contributor

@m-bert m-bert left a comment

Choose a reason for hiding this comment

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

@m-bert It seems like the iOS 26 bug is unrelated to this change. Do you think this one can be merged, or do you prefer both to be fixed in one go?

Seems like it is. Sice this is a one-liner we can merge that and I'll look into 26.0 bug later 😅

@j-piasecki j-piasecki merged commit 924e38f into main Dec 1, 2025
4 checks passed
@j-piasecki j-piasecki deleted the @jpiasecki/fix-pan-finalize-ios branch December 1, 2025 15:44
m-bert added a commit that referenced this pull request Dec 4, 2025
## Description

This PR aims to fix callback differences between `iOS` 26 and 18. Most of them were already resolved, but there were some issues with `Pan` and `onFinalize` callback. 

Calling `reset` has been removed from `Tap` as it is called automatically by recognizer and leaving it resulted in double callbacks.

> [!NOTE]
> For related changes, see #3740, #3756 and #3849.

## Test plan

<details>
<summary>Tested on the following code:</summary>

```tsx
import { StyleSheet, View, Text } from 'react-native';

import {
  GestureHandlerRootView,
  Gesture,
  GestureDetector,
  GestureType,
} from 'react-native-gesture-handler';

function TestBox({
  gestureType,
  bgColor,
}: {
  gestureType: GestureType;
  bgColor: string;
}) {
  const handlerName = gestureType.handlerName;

  const gesture = gestureType
    .onBegin(() => {
      console.log(`[${handlerName}] onBegin`);
    })
    .onEnd((_e, s) => {
      console.log(`[${handlerName}] onEnd (${s})`);
    })
    .onFinalize((_e, s) => {
      console.log(`[${handlerName}] onFinalize (${s})`);
    })
    .runOnJS(true);

  try {
    // @ts-ignore this is exactly why we have the try-catch block
    gesture.onUpdate(() => {
      console.log(`[${handlerName}] onUpdate`);
    });
  } catch {
    /* empty */
  }

  return (
    <View style={styles.center}>
      <Text>{handlerName}</Text>
      <GestureDetector gesture={gesture}>
        <View style={[styles.box, { backgroundColor: bgColor }]} />
      </GestureDetector>
    </View>
  );
}

export default function App() {
  return (
    <GestureHandlerRootView style={[{ flex: 1, padding: 50 }, styles.center]}>
      <TestBox gestureType={Gesture.Pan()} bgColor="#b58df1" />
      <TestBox gestureType={Gesture.LongPress()} bgColor="#f1a85d" />
      <TestBox gestureType={Gesture.Fling()} bgColor="#5df1a8" />
      <TestBox gestureType={Gesture.Tap()} bgColor="#5d8ef1" />
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  center: {
    display: 'flex',
    justifyContent: 'space-around',
    alignItems: 'center',
  },
  box: {
    height: 100,
    width: 100,
    backgroundColor: '#b58df1',
    borderRadius: 20,
    marginBottom: 30,
  },
});
```

</details>
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.

4 participants