Skip to content

Commit ec656ba

Browse files
m-bertj-piasecki
andauthored
[iOS] Fix gestures on iOS 26 (#3756)
## 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 <jakub.piasecki@swmansion.com>
1 parent cd84f44 commit ec656ba

File tree

4 files changed

+11
-12
lines changed

4 files changed

+11
-12
lines changed

packages/react-native-gesture-handler/apple/Handlers/RNFlingHandler.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ - (void)triggerAction
7373

7474
- (void)reset
7575
{
76+
// TODO: On iOS 26 swiping in "wrong" direction doesn't send `onFinalize` callback. This is because now in `reset`
77+
// default state is `UIGestureRecognizerStatePossible`
78+
[self triggerAction]; // Keeping it will not break old iOS because we check if we do not send the same state twice.
7679
[_gestureHandler.pointerTracker reset];
7780
_hasBegan = NO;
7881
[super reset];

packages/react-native-gesture-handler/apple/Handlers/RNLongPressHandler.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ - (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
9595
self.state = UIGestureRecognizerStateFailed;
9696
self.enabled = NO;
9797
self.enabled = YES;
98+
99+
[self triggerAction];
98100
}
99101
}
100102

packages/react-native-gesture-handler/apple/Handlers/RNPanHandler.m

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ - (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
193193
#if !TARGET_OS_TV && !TARGET_OS_OSX
194194
[self tryUpdateStylusData:event];
195195
#endif
196+
[self triggerAction];
196197
}
197198

198199
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
@@ -201,6 +202,7 @@ - (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
201202
#if !TARGET_OS_TV && !TARGET_OS_OSX
202203
[self tryUpdateStylusData:event];
203204
#endif
205+
[self triggerAction];
204206
}
205207

206208
#if TARGET_OS_OSX
@@ -245,16 +247,12 @@ - (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
245247
{
246248
[super touchesEnded:touches withEvent:event];
247249
[self interactionsEnded:touches withEvent:event];
248-
249-
[self triggerAction];
250250
}
251251

252252
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
253253
{
254254
[super touchesCancelled:touches withEvent:event];
255255
[self interactionsCancelled:touches withEvent:event];
256-
257-
[self triggerAction];
258256
}
259257

260258
#endif

packages/react-native-gesture-handler/apple/Handlers/RNTapHandler.m

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ - (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
116116

117117
if ([self shouldFailUnderCustomCriteria]) {
118118
self.state = UIGestureRecognizerStateFailed;
119-
[self triggerAction];
120119
[self reset];
121120
return;
122121
}
@@ -131,8 +130,6 @@ - (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
131130

132131
if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) {
133132
self.state = UIGestureRecognizerStateEnded;
134-
135-
[self triggerAction];
136133
[self reset];
137134
} else {
138135
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDelay];
@@ -144,7 +141,6 @@ - (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
144141
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
145142
self.state = UIGestureRecognizerStateCancelled;
146143

147-
[self triggerAction];
148144
[self reset];
149145
}
150146

@@ -204,16 +200,12 @@ - (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
204200
{
205201
[super touchesEnded:touches withEvent:event];
206202
[self interactionsEnded:touches withEvent:event];
207-
208-
[self triggerAction];
209203
}
210204

211205
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
212206
{
213207
[super touchesCancelled:touches withEvent:event];
214208
[self interactionsCancelled:touches withEvent:event];
215-
216-
[self triggerAction];
217209
}
218210

219211
#endif
@@ -251,6 +243,10 @@ - (BOOL)shouldFailUnderCustomCriteria
251243

252244
- (void)reset
253245
{
246+
if (self.state == UIGestureRecognizerStateFailed) {
247+
[self triggerAction];
248+
}
249+
254250
[_gestureHandler.pointerTracker reset];
255251

256252
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];

0 commit comments

Comments
 (0)