-
-
Notifications
You must be signed in to change notification settings - Fork 1k
[iOS] Fix gestures not ending properly on iOS 26
#3740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| #define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan; | ||
| #define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded; | ||
|
|
||
| #define iOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue] |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
| #define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan; | ||
| #define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded; | ||
|
|
||
| #define iOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue] |
There was a problem hiding this comment.
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.
packages/react-native-gesture-handler/apple/Handlers/RNLongPressHandler.m
Outdated
Show resolved
Hide resolved
|
I'll just mention that we might want to release this fix fast, in a release, as currently pan is broken on all iOS 26 devices. |
|
Any chance this can get merged and pushed in a new release? |
|
@akwasniewski hey there - is there a timeline for a new release? |
I can't give you exact ETA, but it should be the beginning of the next week. |
## Description Some changes in #3740 required follow up. #### Tap Turns out that we can leave `triggerAction` in `reset` (thank you apple for transparent docs that clearly describe what changed between 18.5 and 26 ❤️). It also seems that double `onFinalize` issue is fixed on 26. #### Fling Removing `[self triggerAction]` from `reset` broke things on old iOS. Unfortunately now swiping in different direction does not result in calling `onFinalize` - this should be handled by `triggerAction` in `reset`, but now by default recognizers in `reset` have `Possible` state (once again, thank you apple 😄). To fix that we will probably have to rewrite fling to custom logic. #### LongPress `LongPress` was missing on `triggerAction` call that sends `fail`. #### Pan In `Pan`, I've moved `triggerAction` calls from `touches*` methods to `interactions*` methods. ## Test plan <details> <summary>Tested on basic-example and 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(() => { 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, }, }); ``` </details> --------- Co-authored-by: Jakub Piasecki <[email protected]>
Description
There's a change in gesture recognizers introduced in
iOS26. Now whenresetmethod is called, recognizers go back toUIGestureRecognizerStatePossiblestate. This breaks our current behavior, because this state is mapped intoRNGestureHandlerStateBegan, so if for examplePanfails, it tries to send event withBeganstate.Unfortunately, changing recognizer state is not possible outside of
touches*methods, therefore we had to movetriggerActioninto those callbacks.Let me know if you see a different approach into this problem.
Fixes #3733
Warning
triggerActioncall was already present inTapright beforereset(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:
Test code: