Skip to content

Commit ef7b1d3

Browse files
kkafarkligarski
andauthored
fix(iOS,Fabric): TouchableOpacity does not work on screens with headerTranslucent: true on notchless iOS devices with older OS versions (#2858)
## Description Fixes #2855 The issue is obvious, I'm staggered that we have not caught it much earlier. When `headerconfig.translucent == true` we lay out the `RNSScreenView` underneath the navigation bar & keep sending non-zero content offset to screen shadow node. ## Changes Now, when `headerconfig.translucent == true` we send `contentOffsetY == 0`. ## Test code and steps to reproduce Added `Test2855`. Also should be reproducible on `Test2466` when you set `headerTranslucent: true` on the screen with pressables. ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes --------- Co-authored-by: kligarski <63918941+kligarski@users.noreply.github.com>
1 parent 035837f commit ef7b1d3

5 files changed

Lines changed: 99 additions & 8 deletions

File tree

apps/src/tests/Test2855.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { NavigationContainer, useNavigation } from '@react-navigation/native';
2+
import React, { useState } from 'react';
3+
import { Button, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
4+
5+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
6+
7+
function ProfileScreen() {
8+
const navigation = useNavigation();
9+
const [count, setCount] = useState(0);
10+
const onPress = () => setCount(prevCount => prevCount + 1);
11+
12+
return (
13+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
14+
<Text>Profile Screen</Text>
15+
<Button title="goto Home" onPress={() => navigation.navigate('Home')} />
16+
<View style={styles.countContainer}>
17+
<Text>Count: {count}</Text>
18+
</View>
19+
<TouchableOpacity style={styles.button} onPress={onPress}>
20+
<Text>Press Here</Text>
21+
</TouchableOpacity>
22+
</View>
23+
);
24+
}
25+
26+
function HomeScreen() {
27+
const [count, setCount] = useState(0);
28+
const onPress = () => setCount(prevCount => prevCount + 1);
29+
30+
return (
31+
<View style={styles.container}>
32+
<View style={styles.countContainer}>
33+
<Text>Count: {count}</Text>
34+
</View>
35+
<TouchableOpacity style={styles.button} onPress={onPress}>
36+
<Text>Press Here</Text>
37+
</TouchableOpacity>
38+
</View>
39+
);
40+
}
41+
42+
const styles = StyleSheet.create({
43+
container: {
44+
flex: 1,
45+
justifyContent: 'center',
46+
paddingHorizontal: 10,
47+
},
48+
button: {
49+
alignItems: 'center',
50+
backgroundColor: '#DDDDDD',
51+
padding: 10,
52+
},
53+
countContainer: {
54+
alignItems: 'center',
55+
padding: 10,
56+
},
57+
});
58+
59+
const Stack = createNativeStackNavigator();
60+
61+
function RootStack() {
62+
return (
63+
<Stack.Navigator initialRouteName="Profile">
64+
<Stack.Screen
65+
name="Home"
66+
component={HomeScreen}
67+
options={{
68+
headerTransparent: true,
69+
headerShadowVisible: false,
70+
}}
71+
/>
72+
<Stack.Screen name="Profile" component={ProfileScreen} />
73+
</Stack.Navigator>
74+
);
75+
}
76+
77+
export default function App() {
78+
return (
79+
<NavigationContainer>
80+
<RootStack />
81+
</NavigationContainer>
82+
);
83+
}

apps/src/tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export { default as Test2767 } from './Test2767';
129129
export { default as Test2789 } from './Test2789';
130130
export { default as Test2811 } from './Test2811';
131131
export { default as Test2819 } from './Test2819';
132+
export { default as Test2855 } from './Test2855';
132133
export { default as TestScreenAnimation } from './TestScreenAnimation';
133134
export { default as TestScreenAnimationV5 } from './TestScreenAnimationV5';
134135
export { default as TestHeader } from './TestHeader';

common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ extern const char RNSScreenComponentName[] = "RNSScreen";
1111
Point RNSScreenShadowNode::getContentOriginOffset(
1212
bool /*includeTransform*/) const {
1313
auto stateData = getStateData();
14-
auto contentOffset = stateData.contentOffset;
15-
return {contentOffset.x, contentOffset.y};
14+
return stateData.contentOffset;
1615
}
1716

1817
std::optional<std::reference_wrapper<const ShadowNode::Shared>>

ios/RNSScreen.mm

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,20 @@ - (void)updateBounds
151151
#ifdef RCT_NEW_ARCH_ENABLED
152152
if (_state != nullptr) {
153153
RNSScreenStackHeaderConfig *config = [self findHeaderConfig];
154-
// in large title, ScrollView handles the offset of content so we cannot set it here also.
155-
CGFloat headerHeight =
156-
config.largeTitle ? 0 : [_controller calculateHeaderHeightIsModal:self.isPresentedAsNativeModal];
157-
auto newState =
158-
react::RNSScreenState{RCTSizeFromCGSize(self.bounds.size), RCTPointFromCGPoint(CGPointMake(0, headerHeight))};
154+
155+
// in large title, ScrollView handles the offset of content so we cannot set it here also
156+
// TODO: Why is it assumed in comment above, that large title uses scrollview here? What if only SafeAreaView is
157+
// used?
158+
// When config.translucent == true, we currently use `edgesForExtendedLayout` and the screen is laid out under the
159+
// navigation bar, therefore there is no need to set content offset in shadow tree.
160+
const CGFloat effectiveContentOffsetY = config.largeTitle || config.translucent
161+
? 0
162+
: [_controller calculateHeaderHeightIsModal:self.isPresentedAsNativeModal];
163+
164+
auto newState = react::RNSScreenState{RCTSizeFromCGSize(self.bounds.size), {0, effectiveContentOffsetY}};
159165
_state->updateState(std::move(newState));
166+
167+
// TODO: Requesting layout on every layout is wrong. We should look for a way to get rid of this.
160168
UINavigationController *navctr = _controller.navigationController;
161169
[navctr.view setNeedsLayout];
162170
}

ios/RNSScreenStackHeaderConfig.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ + (void)updateViewController:(UIViewController *)vc
603603
BOOL shouldHide = config == nil || !config.shouldHeaderBeVisible;
604604

605605
if (!shouldHide && !config.translucent) {
606-
// when nav bar is not translucent we chage edgesForExtendedLayout to avoid system laying out
606+
// when nav bar is not translucent we change edgesForExtendedLayout to avoid system laying out
607607
// the screen underneath navigation controllers
608608
vc.edgesForExtendedLayout = UIRectEdgeNone;
609609
} else {

0 commit comments

Comments
 (0)