Skip to content

Commit

Permalink
fix: maintain native behavior of iOS back button with custom style
Browse files Browse the repository at this point in the history
Previously if a custom font or font size was set in the config, a new
`RNSUIBarButtonItem` would be created and set as the back bar button item.
However, this would break native behavior where the button title would shorten
to "Back" if there's not enough space. In iOS 13 it's possible to keep this
native behavior and also style the back button using `UINavigationBarAppearance`.

The only caveat with this approach is that because `UINavigationBarAppearance` is
not available prior to iOS 13, so on older systems we will go ahead and create
a custom `RNSUIBarButtonItem`.

This solves some issues mentioned in software-mansion#1589.
  • Loading branch information
DrOverbuild committed Sep 16, 2022
1 parent 49b24b4 commit 4143d99
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 27 deletions.
3 changes: 2 additions & 1 deletion TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ import Test1473 from './src/Test1473';
import Test1476 from './src/Test1476';
import Test1509 from './src/Test1509';
import Test1539 from './src/Test1539';
import Test1540 from './src/Test1540';

enableFreeze(true);

export default function App() {
return <Test42 />;
return <Test1540 />;
}
66 changes: 66 additions & 0 deletions TestsExample/src/Test1540.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
*/

import React from 'react';
import {Button, Text, View} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackScreenProps,
} from 'react-native-screens/native-stack';

type Screens = {
'Second Screen': undefined;
'Screen With a Ridiculously Long Title and then some': undefined;
};

const HomeScreen = ({navigation, route}: NativeStackScreenProps<Screens>) => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>{route.name}</Text>
<Button
title="Navigate to second screen"
onPress={() => {
navigation.push('Second Screen');
}}
/>
</View>
);
};

const SecondScreen = ({route}: NativeStackScreenProps<Screens>) => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>{route.name}</Text>
</View>
);
};

const Stack = createNativeStackNavigator();

const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Screen With a Ridiculously Long Title and then some"
component={HomeScreen}
/>
<Stack.Screen
name="Second Screen"
component={SecondScreen}
options={{
headerBackTitleStyle: {
fontFamily: 'AvenirNextCondensed-DemiBoldItalic',
},
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};

export default App;
84 changes: 58 additions & 26 deletions ios/RNSScreenStackHeaderConfig.mm
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#ifdef RN_FABRIC_ENABLED
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <React/UIView+React.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
#import <react/renderer/components/rnscreens/EventEmitters.h>
#import <react/renderer/components/rnscreens/Props.h>
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>
#import <React/RCTFabricComponentsPlugins.h>
#else
#import <React/RCTBridge.h>
#import <React/RCTImageLoader.h>
Expand Down Expand Up @@ -241,6 +241,24 @@ + (void)setTitleAttibutes:(NSDictionary *)attrs forButton:(UIBarButtonItem *)but
[button setTitleTextAttributes:attrs forState:UIControlStateFocused];
}

+ (NSMutableDictionary *)backButtonTitleAttributesWithConfig:(RNSScreenStackHeaderConfig *)config
{
NSMutableDictionary *attrs = [NSMutableDictionary new];
NSNumber *size = config.backTitleFontSize ?: @17;
if (config.backTitleFontFamily) {
attrs[NSFontAttributeName] = [RCTFont updateFont:nil
withFamily:config.backTitleFontFamily
size:size
weight:nil
style:nil
variant:nil
scaleMultiplier:1.0];
} else {
attrs[NSFontAttributeName] = [UIFont systemFontOfSize:[size floatValue]];
}
return attrs;
}

+ (UIImage *)loadBackButtonImageInViewController:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
{
#ifdef RN_FABRIC_ENABLED
Expand Down Expand Up @@ -404,6 +422,10 @@ + (UINavigationBarAppearance *)buildAppearance:(UIViewController *)vc
appearance.largeTitleTextAttributes = largeAttrs;
}

if (config.backTitleFontFamily || config.backTitleFontSize) {
appearance.backButtonAppearance.normal.titleTextAttributes = [self backButtonTitleAttributesWithConfig:config];
}

#ifdef RN_FABRIC_ENABLED
[appearance setBackIndicatorImage:nil transitionMaskImage:nil];
#else
Expand Down Expand Up @@ -460,34 +482,32 @@ + (void)updateViewController:(UIViewController *)vc
}

navitem.title = config.title;
#if !TARGET_OS_TV
if (config.backTitle != nil || config.backTitleFontFamily || config.backTitleFontSize ||
config.disableBackButtonMenu) {
RNSUIBarButtonItem *backBarButtonItem = [[RNSUIBarButtonItem alloc] initWithTitle:config.backTitle ?: prevItem.title
style:UIBarButtonItemStylePlain
target:nil
action:nil];

[backBarButtonItem setMenuHidden:config.disableBackButtonMenu];
#if !TARGET_OS_TV
if (config.backTitle) {
prevItem.backButtonTitle = config.backTitle;
}

prevItem.backBarButtonItem = backBarButtonItem;
if (config.backTitleFontFamily || config.backTitleFontSize) {
NSMutableDictionary *attrs = [NSMutableDictionary new];
NSNumber *size = config.backTitleFontSize ?: @17;
if (config.backTitleFontFamily) {
attrs[NSFontAttributeName] = [RCTFont updateFont:nil
withFamily:config.backTitleFontFamily
size:size
weight:nil
style:nil
variant:nil
scaleMultiplier:1.0];
} else {
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:[size floatValue]];
}
[self setTitleAttibutes:attrs forButton:prevItem.backBarButtonItem];
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_14_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
if (@available(iOS 14.0, *)) {
if (config.disableBackButtonMenu) {
RNSUIBarButtonItem *backBarButtonItem =
[[RNSUIBarButtonItem alloc] initWithTitle:config.backTitle ?: prevItem.title
style:UIBarButtonItemStylePlain
target:nil
action:nil];

[backBarButtonItem setMenuHidden:config.disableBackButtonMenu];
prevItem.backBarButtonItem = backBarButtonItem;
// We do not need to set styles for the button here because styles will be configured in
// + (UINavigationBarAppearance *)buildAppearance:withConfig:
} else {
prevItem.backBarButtonItem = nil;
}
} else {
} else
#endif
{
prevItem.backBarButtonItem = nil;
}

Expand Down Expand Up @@ -530,6 +550,18 @@ + (void)updateViewController:(UIViewController *)vc
navctr.navigationBar.backIndicatorImage = nil;
navctr.navigationBar.backIndicatorTransitionMaskImage = nil;
}

// Only set the back back button style if older than iOS 13. If running iOS 13 or later,
// the style is adjusted with + (UINavigationBarAppearance *)buildAppearance:withConfig:
if (config.backTitleFontFamily || config.backTitleFontSize) {
RNSUIBarButtonItem *backBarButtonItem =
[[RNSUIBarButtonItem alloc] initWithTitle:config.backTitle ?: prevItem.title
style:UIBarButtonItemStylePlain
target:nil
action:nil];
[self setTitleAttibutes:[self backButtonTitleAttributesWithConfig:config] forButton:backBarButtonItem];
prevItem.backBarButtonItem = backBarButtonItem;
}
#endif
}
#if !TARGET_OS_TV
Expand Down

0 comments on commit 4143d99

Please sign in to comment.