Skip to content

Conversation

m-bert
Copy link
Contributor

@m-bert m-bert commented Oct 7, 2025

Description

There's a change in gesture recognizers introduced in iOS 26. Now when reset method is called, recognizers go back to UIGestureRecognizerStatePossible state. This breaks our current behavior, because this state is mapped into RNGestureHandlerStateBegan, so if for example Pan fails, it tries to send event with Began state.

Unfortunately, changing recognizer state is not possible outside of touches* methods, therefore we had to move triggerAction into those callbacks.

Let me know if you see a different approach into this problem.

Fixes #3733

Warning

triggerAction call was already present in Tap right before reset (see here). Looks like it was called twice for some reason (but I believe that check for _lastState prevented any problems with this redundancy). For now I have not included second call. If you think it is required, let me know.

Test plan

Tested on the code provided below, on the following platforms:

  • iOS 26.0 (iPhone 17 Pro)
  • iOS 18.5 (iPhone 16e)
  • OSX (macOS 15.6.1)
Test code:
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
    .onEnd(() => {
      console.log(`[${handlerName}] onEnd`);
    })
    .onFinalize(() => {
      console.log(`[${handlerName}] onFinalize`);
    })
    .runOnJS(true);

  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,
  },
});

#define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan;
#define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded;

#define iOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now, since iOS_VERSION macro returns float, we may want to do comparisons up to a constant $\epsilon$, or round this value. I don't think it is necessary at this point, but let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

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

I dont't really see a case where this would be an issue. I know floats behave weirdly, but we will always be checking it via a \leq or \geq comparison with another float and and the mapping from real values to floats is monotonic - it keeps the ordering.

@m-bert m-bert marked this pull request as ready for review October 8, 2025 15:35
@m-bert m-bert requested a review from akwasniewski October 8, 2025 15:56
#define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan;
#define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded;

#define iOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
Copy link
Contributor

Choose a reason for hiding this comment

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

I dont't really see a case where this would be an issue. I know floats behave weirdly, but we will always be checking it via a \leq or \geq comparison with another float and and the mapping from real values to floats is monotonic - it keeps the ordering.

@akwasniewski
Copy link
Contributor

I'll just mention that we might want to release this fix fast, in a release, as currently gh is broken on all iOS 26 devices.

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.

iOS 26: Pan gesture events don't work properly
2 participants