Skip to content

Commit 5ba6cea

Browse files
kkafarkligarski
andauthored
fix(iOS): prevent blocking search bar cancel button by detached header subviews (#2912)
## Description Fixes #2899 When search bar is focused UIKit might temporarily detach our subviews from the window. Currently we just hit test every subview in `_reactSubviews` not taking into account above factor, and therefore leading to situations where search bar cancel button is not clickable. ## Changes We're not hit testing detached subviews now. ## Test code and steps to reproduce Added `Test2899` for this purpose. 1. Click on search bar to focus it, 2. click on cancel button to lose focus. Previously this wouldn't work. ## Bug recording https://github.com/user-attachments/assets/bdc22f87-ed4b-4e9b-98ef-8a5d8ad23c9f ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes --------- Co-authored-by: kligarski <[email protected]>
1 parent 0e2fd32 commit 5ba6cea

5 files changed

Lines changed: 64 additions & 0 deletions

File tree

apps/src/shared/Rectangle.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { ColorValue, View, ViewProps, ViewStyle } from 'react-native';
3+
4+
export interface RectangleProps extends ViewProps {
5+
color?: ColorValue,
6+
width?: ViewStyle['width'],
7+
height?: ViewStyle['height'],
8+
}
9+
10+
export function Rectangle(props: RectangleProps) {
11+
const { color, width, height, style, ...remainingProps } = props;
12+
return <View style={[{ backgroundColor: color, width, height }, style]} {...remainingProps} />;
13+
}
14+

apps/src/shared/styles.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { StyleSheet } from 'react-native';
2+
3+
export const styles = StyleSheet.create({
4+
flexContainer: {
5+
flex: 1,
6+
},
7+
});

apps/src/tests/Test2899.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { NavigationContainer } from '@react-navigation/native';
2+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
3+
import React from 'react';
4+
import { styles } from '../shared/styles';
5+
import { View } from 'react-native';
6+
import { Rectangle } from '../shared/Rectangle';
7+
8+
const Stack = createNativeStackNavigator();
9+
10+
function Home() {
11+
return (
12+
<View style={[styles.flexContainer, { backgroundColor: 'darkorange' }]} />
13+
);
14+
}
15+
16+
function HeaderRight() {
17+
return (
18+
<Rectangle width={128} height={36} color={'darkblue'} />
19+
);
20+
}
21+
22+
function App() {
23+
return (
24+
<NavigationContainer>
25+
<Stack.Navigator>
26+
<Stack.Screen name="Home" component={Home} options={{
27+
headerRight: HeaderRight,
28+
headerSearchBarOptions: {},
29+
}} />
30+
</Stack.Navigator>
31+
</NavigationContainer>
32+
);
33+
}
34+
35+
export default App;

apps/src/tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export { default as Test2811 } from './Test2811';
133133
export { default as Test2819 } from './Test2819';
134134
export { default as Test2855 } from './Test2855';
135135
export { default as Test2877 } from './Test2877'; // [E2E created](iOS): issue is related to formSheet on iOS
136+
export { default as Test2899 } from './Test2899';
136137
export { default as TestScreenAnimation } from './TestScreenAnimation';
137138
export { default as TestScreenAnimationV5 } from './TestScreenAnimationV5';
138139
export { default as TestHeader } from './TestHeader';

ios/RNSScreenStackHeaderConfig.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
162162
{
163163
for (RNSScreenStackHeaderSubview *subview in _reactSubviews) {
164164
if (subview.type == RNSScreenStackHeaderSubviewTypeLeft || subview.type == RNSScreenStackHeaderSubviewTypeRight) {
165+
// E.g. presence of focused search bar might cause the subviews to be temporarily unmounted & we don't want
166+
// them to be touch targets then, otherwise we might e.g. block cancel button.
167+
// See: https://github.com/software-mansion/react-native-screens/issues/2899
168+
if (subview.window == nil) {
169+
continue;
170+
}
171+
165172
// we wrap the headerLeft/Right component in a UIBarButtonItem
166173
// so we need to hit test subviews from left to right, because of the view flattening
167174
UIView *headerComponent = nil;

0 commit comments

Comments
 (0)