-
-
Notifications
You must be signed in to change notification settings - Fork 498
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(Fabric): back gesture activates
Pressable
elements (#2131)
## Description When cancelling touches we previously relied on `touchHandler` instance being exposed by some react managed view above us (screen stack) in the view hierarchy (on Paper it was `RCTRootContentView`). However this changed with some recent Fabric version and no view exposes such property any longer, despite the fact that touch handler is obviously still utilised by these views even on Fabric, but the field has been removed from public API. Currently on Fabric (as of version `0.74.1`) `RCTSurfaceTouchHandler` is owned by instance of `RCTFabricSurface` owned by instance of `RCTSurfaceView` which we can expect to live above us in the view hierarchy (RN mounts it in the very beginning of application runtime). To get access to the private touchHandler (which is a `UIGestureRecognizer`) we observe that it is attached to the `RCTSurfaceView` and thus it is retained in its `gestureRecognizer` array (native API). > [!tip] > We could potentially cache the instance of `RCTSurfaceView`, but for now I've come to a conclusion that this is an minor minor computation (there is only one gesture recognizer in the array & the parent lookup is O(log n) (I know this is not a balanced tree, but you know what I mean). However this is always an option for future. > [!note] > I thought of adding warning in case of `RCTSurfaceView` being not present above screen stack in view hierarchy but this might be the case with `modal` stack presentation. Closes #2118 ## Changes * Added utility extensions on `RCTTouchHandler` & `RCTSurfaceTouchHandler` classes to make the code more expressive * Splited `RNSScreenStackView.cancelTouchesInParent` implementation into two separate for Paper & Fabric * In case of Fabric added lookup for `RCTSurfaceView` & got access to its touch handler. ## Test code and steps to reproduce `Test2118` See #2118 for issue description. ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
- Loading branch information
Showing
9 changed files
with
273 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import React from 'react'; | ||
import { View, Text, Button, Pressable, StyleSheet, Alert } from 'react-native'; | ||
import { NavigationContainer, useNavigation } from '@react-navigation/native'; | ||
import { createNativeStackNavigator } from '@react-navigation/native-stack'; | ||
|
||
const Stack = createNativeStackNavigator(); // <-- change to createStackNavigator to see a difference | ||
|
||
const ModalStack = createNativeStackNavigator(); | ||
|
||
export default function App() { | ||
return ( | ||
<NavigationContainer> | ||
<Stack.Navigator screenOptions={{ animation: 'slide_from_left' }}> | ||
<Stack.Screen name="Home" component={HomeScreen} /> | ||
<Stack.Screen name="DetailsStack" component={DetailsScreen} /> | ||
<Stack.Screen | ||
name="StackInModal" | ||
component={StackInModal} | ||
options={{ presentation: 'modal' }} | ||
/> | ||
</Stack.Navigator> | ||
</NavigationContainer> | ||
); | ||
} | ||
|
||
function HomeScreen() { | ||
const navigation = useNavigation(); | ||
return ( | ||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> | ||
<Text>Home Screen</Text> | ||
<Button | ||
title="Go to Details" | ||
onPress={() => navigation.navigate('DetailsStack')} | ||
/> | ||
<Button | ||
title="Go to StackInModal" | ||
onPress={() => navigation.navigate('StackInModal')} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function DetailsScreen() { | ||
return ( | ||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> | ||
{new Array(10).fill(0).map((_, i) => ( | ||
<Pressable | ||
key={i.toString()} | ||
onPress={() => { | ||
Alert.alert('Pressed!'); | ||
}} | ||
style={({ pressed }) => [ | ||
{ | ||
backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white', | ||
}, | ||
styles.wrapperCustom, | ||
]}> | ||
{({ pressed }) => ( | ||
<Text style={styles.text}>{pressed ? 'Pressed!' : 'Press Me'}</Text> | ||
)} | ||
</Pressable> | ||
))} | ||
</View> | ||
); | ||
} | ||
|
||
function StackInModal() { | ||
return ( | ||
<ModalStack.Navigator> | ||
<ModalStack.Screen name="ModalHome" component={HomeScreen} /> | ||
<ModalStack.Screen name="ModalDetails" component={DetailsScreen} /> | ||
</ModalStack.Navigator> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
wrapperCustom: { | ||
width: '100%', | ||
height: 100, | ||
marginHorizontal: 30, | ||
borderRadius: 10, | ||
margin: 10, | ||
}, | ||
text: { | ||
fontSize: 20, | ||
color: 'black', | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import React from 'react'; | ||
import { View, Text, Button, Pressable, StyleSheet, Alert } from 'react-native'; | ||
import { NavigationContainer, useNavigation } from '@react-navigation/native'; | ||
import { createNativeStackNavigator } from '@react-navigation/native-stack'; | ||
|
||
const Stack = createNativeStackNavigator(); // <-- change to createStackNavigator to see a difference | ||
|
||
const ModalStack = createNativeStackNavigator(); | ||
|
||
export default function App() { | ||
return ( | ||
<NavigationContainer> | ||
<Stack.Navigator screenOptions={{ animation: 'slide_from_left' }}> | ||
<Stack.Screen name="Home" component={HomeScreen} /> | ||
<Stack.Screen name="DetailsStack" component={DetailsScreen} /> | ||
<Stack.Screen | ||
name="StackInModal" | ||
component={StackInModal} | ||
options={{ presentation: 'modal' }} | ||
/> | ||
</Stack.Navigator> | ||
</NavigationContainer> | ||
); | ||
} | ||
|
||
function HomeScreen() { | ||
const navigation = useNavigation(); | ||
return ( | ||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> | ||
<Text>Home Screen</Text> | ||
<Button | ||
title="Go to Details" | ||
onPress={() => navigation.navigate('DetailsStack')} | ||
/> | ||
<Button | ||
title="Go to StackInModal" | ||
onPress={() => navigation.navigate('StackInModal')} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function DetailsScreen() { | ||
return ( | ||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> | ||
{new Array(10).fill(0).map((_, i) => ( | ||
<Pressable | ||
key={i.toString()} | ||
onPress={() => { | ||
Alert.alert('Pressed!'); | ||
}} | ||
style={({ pressed }) => [ | ||
{ | ||
backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white', | ||
}, | ||
styles.wrapperCustom, | ||
]}> | ||
{({ pressed }) => ( | ||
<Text style={styles.text}>{pressed ? 'Pressed!' : 'Press Me'}</Text> | ||
)} | ||
</Pressable> | ||
))} | ||
</View> | ||
); | ||
} | ||
|
||
function StackInModal() { | ||
return ( | ||
<ModalStack.Navigator> | ||
<ModalStack.Screen name="ModalHome" component={HomeScreen} /> | ||
<ModalStack.Screen name="ModalDetails" component={DetailsScreen} /> | ||
</ModalStack.Navigator> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
wrapperCustom: { | ||
width: '100%', | ||
height: 100, | ||
marginHorizontal: 30, | ||
borderRadius: 10, | ||
margin: 10, | ||
}, | ||
text: { | ||
fontSize: 20, | ||
color: 'black', | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#ifdef RCT_NEW_ARCH_ENABLED | ||
|
||
#import <React/RCTSurfaceTouchHandler.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RCTSurfaceTouchHandler (RNSUtility) | ||
|
||
- (void)rnscreens_cancelTouches; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END | ||
|
||
#endif // RCT_NEW_ARCH_ENABLED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#ifdef RCT_NEW_ARCH_ENABLED | ||
#import "RCTSurfaceTouchHandler+RNSUtility.h" | ||
|
||
@implementation RCTSurfaceTouchHandler (RNSUtility) | ||
|
||
- (void)rnscreens_cancelTouches | ||
{ | ||
[self setEnabled:NO]; | ||
[self setEnabled:YES]; | ||
[self reset]; | ||
} | ||
|
||
@end | ||
#endif // RCT_NEW_ARCH_ENABLED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#ifndef RCT_NEW_ARCH_ENABLED | ||
|
||
#import <React/RCTTouchHandler.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RCTTouchHandler (RNSUtility) | ||
|
||
- (void)rnscreens_cancelTouches; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END | ||
|
||
#endif // !RCT_NEW_ARCH_ENABLED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#ifndef RCT_NEW_ARCH_ENABLED | ||
#import "RCTTouchHandler+RNSUtility.h" | ||
|
||
@implementation RCTTouchHandler (RNSUtility) | ||
|
||
- (void)rnscreens_cancelTouches | ||
{ | ||
[self setEnabled:NO]; | ||
[self setEnabled:YES]; | ||
[self reset]; | ||
} | ||
|
||
@end | ||
|
||
#endif // !RCT_NEW_ARCH_ENABLED |