Skip to content

Commit b5612bf

Browse files
fix(iOS): back button not respecting style options (#1726)
## Description @kkafar: #1646 introduced a bug, where if iOS version >= 14 styles for back title would not get applied, due to [ill defined `if/else-if` logic](https://github.com/software-mansion/react-native-screens/blob/5e1f7ecdf30573f3e441e73c4f1f05c1f3ab9ccb/ios/RNSScreenStackHeaderConfig.mm#L477-L491). ## Changes @kkafar: Removed the fix introduced in #1646 and replaced it with setting `backBarButtonItem`'s title to `nil` & `backBarButtonTitle` to appropriate value when back button title is supposed to be invisible. This results in no title being displayed next to back button, but the correct value is present in back menu (if one is enabled). To achieve the demanded effect I needed to distinguish between states: 1. back title visible or not next to back button 2. back title is nil / empty / whitespace only Currently when `headerBackTitleVisible: false` is set in JS, [whitespace only string is send to native side](https://github.com/software-mansion/react-native-screens/blob/5e1f7ecdf30573f3e441e73c4f1f05c1f3ab9ccb/src/native-stack/views/HeaderConfig.tsx#L105) (same thing [happens in `react-navigation`](https://github.com/react-navigation/react-navigation/blob/2ec98f3fae051c60e76aed3698d1bca6bbf730ef/packages/native-stack/src/views/HeaderConfig.tsx#L171)), but the same information can be passed to native side when user sets: ```js headerBackTitleVisible: true, headerBackTitle: ' ' ``` so we can not really determine on native side whether the back button title should be visible or not (in case of empty string / nil passed from the user we default to title of previous screen). Therefore I decided to pass `headerBackTitleVisible` prop value directly to the native side via **new prop `backTitleVisible` on `ScreenStackHeaderConfig` component** and base new logic on its (props) value. ## Screenshots / GIFs ### Before https://user-images.githubusercontent.com/32227697/221241945-58d3bb51-f25a-4d0f-b74f-a4e18792535b.mp4 ### After https://user-images.githubusercontent.com/32227697/221241962-b25108c9-beb5-4bbb-b712-3830dfc1c250.mp4 ## Test code and steps to reproduce `Test1726` in `FabricTestExample` & `TestsExample` ## Checklist - [x] Included code example that can be used to test this change - [x] Updated TS types - [x] Updated documentation: <!-- For adding new props to native-stack --> - [x] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [x] Ensured that CI passes --------- Co-authored-by: Kacper Kafara <kacperkafara@gmail.com>
1 parent ab9c74c commit b5612bf

File tree

18 files changed

+185
-38
lines changed

18 files changed

+185
-38
lines changed

FabricTestExample/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import Test1476 from './src/Test1476';
8484
import Test1646 from './src/Test1646';
8585
import Test1649 from './src/Test1649';
8686
import Test1683 from './src/Test1683';
87+
import Test1726 from './src/Test1726';
8788

8889
enableFreeze(true);
8990

FabricTestExample/src/Test1646.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {NavigationContainer, useRoute} from '@react-navigation/native';
2-
import {createNativeStackNavigator} from '@react-navigation/native-stack';
2+
// import {createNativeStackNavigator} from '@react-navigation/native-stack';
3+
import {createNativeStackNavigator} from 'react-native-screens/native-stack';
34
import * as React from 'react';
45
import {Button, View} from 'react-native';
56

@@ -11,6 +12,8 @@ export default function App() {
1112
<Stack.Navigator
1213
screenOptions={{
1314
headerBackTitleVisible: false,
15+
// headerBackTitle: 'NavHeader',
16+
// disableBackButtonMenu: true,
1417
}}>
1518
<Stack.Screen
1619
name="Screen"
@@ -30,6 +33,10 @@ function Screen({navigation}: any) {
3033
// navigation.setOptions({headerBackTitleVisible: count % 2 === 0});
3134
// }, [count, navigation]);
3235

36+
// React.useEffect(() => {
37+
// navigation.setOptions({headerBackTitle: `Custom ${count}`})
38+
// }, [count, navigation]);
39+
3340
return (
3441
<View>
3542
<Button

FabricTestExample/src/Test1726.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {NavigationContainer} from '@react-navigation/native';
2+
import React from 'react';
3+
import {Button, View} from 'react-native';
4+
import {createNativeStackNavigator} from 'react-native-screens/native-stack';
5+
6+
const RootStack = createNativeStackNavigator();
7+
8+
const Screen1 = ({navigation: {navigate}}) => (
9+
<View style={{flex: 1}}>
10+
<Button onPress={() => navigate('Screen2')} title="Screen 2" />
11+
</View>
12+
);
13+
14+
const Screen2 = ({navigation: {navigate}}) => (
15+
<View style={{flex: 1}}>
16+
<Button onPress={() => navigate('Screen3')} title="Screen 3" />
17+
</View>
18+
);
19+
20+
const Screen3 = () => <View style={{flex: 1}} />;
21+
22+
const App = () => {
23+
return (
24+
<NavigationContainer>
25+
<RootStack.Navigator>
26+
<RootStack.Screen component={Screen1} name="Screen1" />
27+
<RootStack.Screen
28+
component={Screen2}
29+
name="Screen2"
30+
options={{
31+
headerBackTitle: 'Custom title',
32+
}}
33+
/>
34+
<RootStack.Screen
35+
component={Screen3}
36+
name="Screen3"
37+
options={{
38+
headerBackTitle: 'Small title',
39+
headerBackTitleStyle: {fontSize: 8},
40+
}}
41+
/>
42+
</RootStack.Navigator>
43+
</NavigationContainer>
44+
);
45+
};
46+
47+
export default App;

TestsExample/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import Test1539 from './src/Test1539';
8686
import Test1646 from './src/Test1646';
8787
import Test1649 from './src/Test1649';
8888
import Test1683 from './src/Test1683';
89+
import Test1726 from './src/Test1726';
8990

9091
enableFreeze(true);
9192

TestsExample/src/Test1646.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {NavigationContainer, useRoute} from '@react-navigation/native';
2-
import {createNativeStackNavigator} from '@react-navigation/native-stack';
2+
// import {createNativeStackNavigator} from '@react-navigation/native-stack';
3+
import {createNativeStackNavigator} from 'react-native-screens/native-stack';
34
import * as React from 'react';
45
import {Button, View} from 'react-native';
56

@@ -11,6 +12,8 @@ export default function App() {
1112
<Stack.Navigator
1213
screenOptions={{
1314
headerBackTitleVisible: false,
15+
// headerBackTitle: 'NavHeader',
16+
// disableBackButtonMenu: true,
1417
}}>
1518
<Stack.Screen
1619
name="Screen"
@@ -30,6 +33,10 @@ function Screen({navigation}: any) {
3033
// navigation.setOptions({headerBackTitleVisible: count % 2 === 0});
3134
// }, [count, navigation]);
3235

36+
// React.useEffect(() => {
37+
// navigation.setOptions({headerBackTitle: `Custom ${count}`})
38+
// }, [count, navigation]);
39+
3340
return (
3441
<View>
3542
<Button

TestsExample/src/Test1726.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NavigationContainer } from "@react-navigation/native";
2+
import React from "react";
3+
import { Button, View } from "react-native";
4+
import { createNativeStackNavigator } from "react-native-screens/native-stack";
5+
6+
const RootStack = createNativeStackNavigator();
7+
8+
const Screen1 = ({navigation: {navigate}}) => (
9+
<View style={{flex: 1}}>
10+
<Button onPress={() => navigate('Screen2')} title="Screen 2" />
11+
</View>
12+
);
13+
14+
const Screen2 = ({navigation: {navigate}}) => (
15+
<View style={{flex: 1}}>
16+
<Button onPress={() => navigate('Screen3')} title="Screen 3" />
17+
</View>
18+
);
19+
20+
const Screen3 = () => <View style={{flex: 1}} />;
21+
22+
const App = () => {
23+
return (
24+
<NavigationContainer>
25+
<RootStack.Navigator>
26+
<RootStack.Screen component={Screen1} name="Screen1" />
27+
<RootStack.Screen
28+
component={Screen2}
29+
name="Screen2"
30+
options={{
31+
headerBackTitle: 'Custom title',
32+
}}
33+
/>
34+
<RootStack.Screen
35+
component={Screen3}
36+
name="Screen3"
37+
options={{
38+
headerBackTitle: 'Small title',
39+
headerBackTitleStyle: {fontSize: 8},
40+
}}
41+
/>
42+
</RootStack.Navigator>
43+
</NavigationContainer>
44+
);
45+
};
46+
47+
export default App;

android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ class ScreenStackHeaderConfigViewManager : ViewGroupManager<ScreenStackHeaderCon
162162
logNotAvailable("backTitleFontSize")
163163
}
164164

165+
override fun setBackTitleVisible(view: ScreenStackHeaderConfig?, value: Boolean) {
166+
logNotAvailable("backTitleVisible")
167+
}
168+
165169
override fun setLargeTitle(view: ScreenStackHeaderConfig?, value: Boolean) {
166170
logNotAvailable("largeTitle")
167171
}

android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerDelegate.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
3434
case "backTitleFontSize":
3535
mViewManager.setBackTitleFontSize(view, value == null ? 0 : ((Double) value).intValue());
3636
break;
37+
case "backTitleVisible":
38+
mViewManager.setBackTitleVisible(view, value == null ? true : (boolean) value);
39+
break;
3740
case "color":
3841
mViewManager.setColor(view, ColorPropConverter.getColor(value, view.getContext()));
3942
break;

android/src/paper/java/com/facebook/react/viewmanagers/RNSScreenStackHeaderConfigManagerInterface.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public interface RNSScreenStackHeaderConfigManagerInterface<T extends View> {
1717
void setBackTitle(T view, @Nullable String value);
1818
void setBackTitleFontFamily(T view, @Nullable String value);
1919
void setBackTitleFontSize(T view, int value);
20+
void setBackTitleVisible(T view, boolean value);
2021
void setColor(T view, @Nullable Integer value);
2122
void setDirection(T view, @Nullable String value);
2223
void setHidden(T view, boolean value);

createNativeStackNavigator/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Whether you can use gestures to dismiss this screen. Defaults to `true`.
9999

100100
#### `headerBackTitle`
101101

102-
Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle`.
102+
Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle` when not set or set to whitespace only value.
103103

104104
#### `headerBackTitleStyle`
105105

0 commit comments

Comments
 (0)