Skip to content
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

fix(iOS): not working hitslop for headerRight/Left views #1995

Merged
merged 4 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FabricTestExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import Test1802 from './src/Test1802';
import Test1829 from './src/Test1829';
import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1981 from './src/Test1981';

enableFreeze(true);

Expand Down
103 changes: 103 additions & 0 deletions FabricTestExample/src/Test1981.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import { NavigationContainer, NavigationContext, ParamListBase } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackNavigationProp } from '@react-navigation/native-stack';
import { View, StyleSheet, Button, Pressable, Text } from 'react-native';

type NavProp = {
navigation: NativeStackNavigationProp<ParamListBase>;
};

const Stack = createNativeStackNavigator();

function FirstScreen({ navigation }: NavProp) {
const navigateToSecond = () => {
navigation.navigate('Second');
};
return (
<View style={[styles.redbox, styles.centeredView]}>
<Button title="Navigate to Second" onPress={navigateToSecond} />
<PressableWithHitSlop />
</View>
);
}

function SecondScreen({ navigation }: NavProp) {
const navigateToFirst = () => {
navigation.navigate('First');
};

return (
<View style={[styles.greenbox, styles.centeredView]}>
<Button title="Navigate to First" onPress={navigateToFirst} />
</View>
);
}

function HeaderLeft() {
const onPressCallback = () => {
console.log('HeaderLeft onPressCallback invoked');
};

return (
<Pressable style={[styles.bluebox]} hitSlop={12} onPress={onPressCallback}>
<Text style={{ color: 'white' }}>Press me</Text>
</Pressable>
);
}

function PressableWithHitSlop() {
const onPressCallback = () => {
console.log('PressableWithHitSlop onPressCallback invoked');
};

return (
<View
style={{
padding: 12,
margin: -12,
backgroundColor: 'yellow',
}}>
<Pressable
style={[styles.greenbox]}
hitSlop={12}
onPress={onPressCallback}>
<Text style={{ color: 'white' }}>Press me</Text>
</Pressable>
</View>
);
}

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="First"
component={FirstScreen}
options={{
headerLeft: () => HeaderLeft(),
headerRight: () => PressableWithHitSlop(),
}}
/>
<Stack.Screen name="Second" component={SecondScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
redbox: {
backgroundColor: 'red',
},
greenbox: {
backgroundColor: 'green',
},
bluebox: {
backgroundColor: 'blue',
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import Test1802 from './src/Test1802';
import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1829 from './src/Test1829';
import Test1981 from './src/Test1981';

enableFreeze(true);

Expand Down
103 changes: 103 additions & 0 deletions TestsExample/src/Test1981.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import { NavigationContainer, NavigationContext, ParamListBase } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackNavigationProp } from '@react-navigation/native-stack';
import { View, StyleSheet, Button, Pressable, Text } from 'react-native';

type NavProp = {
navigation: NativeStackNavigationProp<ParamListBase>;
};

const Stack = createNativeStackNavigator();

function FirstScreen({ navigation }: NavProp) {
const navigateToSecond = () => {
navigation.navigate('Second');
};
return (
<View style={[styles.redbox, styles.centeredView]}>
<Button title="Navigate to Second" onPress={navigateToSecond} />
<PressableWithHitSlop />
</View>
);
}

function SecondScreen({ navigation }: NavProp) {
const navigateToFirst = () => {
navigation.navigate('First');
};

return (
<View style={[styles.greenbox, styles.centeredView]}>
<Button title="Navigate to First" onPress={navigateToFirst} />
</View>
);
}

function HeaderLeft() {
const onPressCallback = () => {
console.log('HeaderLeft onPressCallback invoked');
};

return (
<Pressable style={[styles.bluebox]} hitSlop={12} onPress={onPressCallback}>
<Text style={{ color: 'white' }}>Press me</Text>
</Pressable>
);
}

function PressableWithHitSlop() {
const onPressCallback = () => {
console.log('PressableWithHitSlop onPressCallback invoked');
};

return (
<View
style={{
padding: 12,
margin: -12,
backgroundColor: 'yellow',
}}>
<Pressable
style={[styles.greenbox]}
hitSlop={12}
onPress={onPressCallback}>
<Text style={{ color: 'white' }}>Press me</Text>
</Pressable>
</View>
);
}

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="First"
component={FirstScreen}
options={{
headerLeft: () => HeaderLeft(),
headerRight: () => PressableWithHitSlop(),
}}
/>
<Stack.Screen name="Second" component={SecondScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
redbox: {
backgroundColor: 'red',
},
greenbox: {
backgroundColor: 'green',
},
bluebox: {
backgroundColor: 'blue',
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
8 changes: 7 additions & 1 deletion ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -560,13 +560,19 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio
}
}

- (RNSScreenStackHeaderConfig *_Nullable)findHeaderConfig
- (nullable RNSScreenStackHeaderConfig *)findHeaderConfig
{
// Fast path
if ([self.reactSubviews.lastObject isKindOfClass:RNSScreenStackHeaderConfig.class]) {
return (RNSScreenStackHeaderConfig *)self.reactSubviews.lastObject;
}

for (UIView *view in self.reactSubviews) {
if ([view isKindOfClass:RNSScreenStackHeaderConfig.class]) {
return (RNSScreenStackHeaderConfig *)view;
}
}

return nil;
}

Expand Down
4 changes: 2 additions & 2 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,8 @@ - (BOOL)isInGestureResponseDistance:(UIGestureRecognizer *)gestureRecognizer top
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(_controller.navigationBar.frame, point)) {
// headerConfig should be the first subview of the topmost screen
UIView *headerConfig = [[_reactSubviews.lastObject reactSubviews] firstObject];
RNSScreenView *topMostScreen = (RNSScreenView *)_reactSubviews.lastObject;
UIView *headerConfig = topMostScreen.findHeaderConfig;
if ([headerConfig isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
UIView *headerHitTestResult = [headerConfig hitTest:point withEvent:event];
if (headerHitTestResult != nil) {
Expand Down
Loading