Skip to content

Commit 4230010

Browse files
committed
add backButtonUseModernImplementation prop to keep backward compatibility
1 parent 1803ec8 commit 4230010

5 files changed

Lines changed: 121 additions & 3 deletions

File tree

guides/GUIDE_FOR_LIBRARY_AUTHORS.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ Controls the color of the navigation header.
523523

524524
### `backTitle` (iOS only)
525525

526-
Allows for controlling the string to be rendered next to back button. For iOS versions prior to 26, the title of the previous screen is used as default.
526+
Allows for controlling the string to be rendered next to back button. The title of the previous screen is used as default if `backButtonUseModernImplementation` is set to `false` or if `backButtonUseModernImplementation` is set to `true` and iOS version prior to 26 is used.
527527

528528
### `backTitleFontFamily` (iOS only)
529529

@@ -535,7 +535,7 @@ Allows for customizing font size to be used for back button title on iOS.
535535

536536
### `backTitleVisible` (iOS only)
537537

538-
This prop has been **deprecated**. Setting it has no effect as native code related to this prop has been removed. Kept only for backward compatibility. Will be removed in next major release.
538+
This prop has been **deprecated**. Kept only for backward compatibility, is respected only if `backButtonUseModernImplementation` is set to `false`.
539539

540540
Whether the back button title should be visible. Defaults to `true`.
541541

@@ -577,6 +577,17 @@ On iOS versions prior to 26, using `generic` display mode with `backTitleFontFam
577577
or `disableBackButtonMenu` property set is not supported due to limitations in the native platform.
578578
In such cases, display mode will fallback to `default`.
579579

580+
If `backButtonUseModernImplementation` is set to `false`, this prop is used only when none of: `backTitleFontFamily`,
581+
`backTitleFontSize`, `disableBackButtonMenu` and `backTitleVisible=false` is set.
582+
583+
### `backButtonUseModernImplementation` (iOS only)
584+
585+
Used for backward compatibility only. Setting this prop to `false` is recommended.
586+
587+
Uses previous implementation of native back button configuration.
588+
589+
Default is `true`.
590+
580591
### `headerLeftBarButtonItems` / `headerRightBarButtonsItems` (iOS only)
581592

582593
An array of objects describing native bar button items to display on the left or right side of the header. Each item can be:

ios/RNSScreenStackHeaderConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
5858
@property (nonatomic) BOOL backButtonInCustomView;
5959
@property (nonatomic) UISemanticContentAttribute direction;
6060
@property (nonatomic) UINavigationItemBackButtonDisplayMode backButtonDisplayMode;
61+
@property (nonatomic) BOOL backButtonUseModernImplementation;
6162
@property (nonatomic) RNSBlurEffectStyle blurEffect;
6263
@property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerRightBarButtonItems;
6364
@property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerLeftBarButtonItems;

ios/RNSScreenStackHeaderConfig.mm

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,18 @@ + (void)updateViewController:(UIViewController *)vc
747747

748748
- (void)configureBackItem:(nullable UINavigationItem *)prevItem
749749
withPrevVC:(nullable UIViewController *)prevVC API_UNAVAILABLE(tvos)
750+
{
751+
const auto *config = self;
752+
753+
if (config.backButtonUseModernImplementation) {
754+
[self configureModernBackItem:prevItem withPrevVC:prevVC];
755+
} else {
756+
[self configureLegacyBackItem:prevItem withPrevVC:prevVC];
757+
}
758+
}
759+
760+
- (void)configureModernBackItem:(nullable UINavigationItem *)prevItem
761+
withPrevVC:(nullable UIViewController *)prevVC API_UNAVAILABLE(tvos)
750762
{
751763
#if !TARGET_OS_TV
752764
if (prevItem == nil) {
@@ -898,6 +910,79 @@ - (void)configureNativeBackItem:(nonnull UINavigationItem *)prevItem API_UNAVAIL
898910
prevItem.backBarButtonItem = nil;
899911
}
900912

913+
- (void)configureLegacyBackItem:(nullable UINavigationItem *)prevItem
914+
withPrevVC:(nullable UIViewController *)prevVC API_UNAVAILABLE(tvos)
915+
{
916+
#if !TARGET_OS_TV
917+
if (prevItem == nil) {
918+
return;
919+
}
920+
921+
const auto *config = self;
922+
923+
const auto isBackTitleBlank = [NSString rnscreens_isBlankOrNull:config.backTitle] == YES;
924+
NSString *resolvedBackTitle = isBackTitleBlank ? prevItem.title : config.backTitle;
925+
926+
// If previous screen controller was recreated (e.g. when you go back to tab with stack that has multiple screens),
927+
// its navigationItem may not have any information from screen's headerConfig, including the title.
928+
// If this is the case, we attempt to extract the title from previous screen's config directly.
929+
if (resolvedBackTitle == nil && [prevVC isKindOfClass:[RNSScreen class]]) {
930+
RNSScreen *prevScreen = static_cast<RNSScreen *>(prevVC);
931+
resolvedBackTitle = prevScreen.screenView.findHeaderConfig.title;
932+
}
933+
934+
prevItem.backButtonTitle = resolvedBackTitle;
935+
// This has any effect only in case the `backBarButtonItem` is not set.
936+
// We apply it before we configure the back item, because it might get overriden.
937+
prevItem.backButtonDisplayMode = config.backButtonDisplayMode;
938+
939+
if (config.isBackTitleVisible) {
940+
RNSBackBarButtonItem *backBarButtonItem = [[RNSBackBarButtonItem alloc] initWithTitle:resolvedBackTitle
941+
style:UIBarButtonItemStylePlain
942+
target:nil
943+
action:nil];
944+
auto shouldUseCustomBackBarButtonItem = config.disableBackButtonMenu;
945+
[backBarButtonItem setMenuHidden:config.disableBackButtonMenu];
946+
947+
if ((config.backTitleFontFamily &&
948+
// While being used by react-navigation, the `backTitleFontFamily` will
949+
// be set to "System" by default - which is the system default font.
950+
// To avoid always considering the font as customized, we need to have an additional check.
951+
// See: https://github.com/software-mansion/react-native-screens/pull/2105#discussion_r1565222738
952+
![config.backTitleFontFamily isEqual:@"System"]) ||
953+
config.backTitleFontSize) {
954+
shouldUseCustomBackBarButtonItem = YES;
955+
NSMutableDictionary *attrs = [NSMutableDictionary new];
956+
NSNumber *size = config.backTitleFontSize ?: @17;
957+
if (config.backTitleFontFamily) {
958+
attrs[NSFontAttributeName] = [RCTFont updateFont:nil
959+
withFamily:config.backTitleFontFamily
960+
size:size
961+
weight:nil
962+
style:nil
963+
variant:nil
964+
scaleMultiplier:1.0];
965+
} else {
966+
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:[size floatValue]];
967+
}
968+
[RNSScreenStackHeaderConfig setTitleAttibutes:attrs forButton:backBarButtonItem];
969+
}
970+
971+
// Prevent unnecessary assignment of backBarButtonItem if it is not customized,
972+
// as assigning one will override the native behavior of automatically shortening
973+
// the title to "Back" or hide the back title if there's not enough space.
974+
// See: https://github.com/software-mansion/react-native-screens/issues/1589
975+
if (shouldUseCustomBackBarButtonItem) {
976+
prevItem.backBarButtonItem = backBarButtonItem;
977+
}
978+
} else {
979+
// back button title should be not visible next to back button,
980+
// but it should still appear in back menu
981+
prevItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal;
982+
}
983+
#endif
984+
}
985+
901986
- (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *)navCtrl
902987
{
903988
if ((self.direction == UISemanticContentAttributeForceLeftToRight ||
@@ -1216,6 +1301,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
12161301
}
12171302

12181303
_backTitleVisible = newScreenProps.backTitleVisible;
1304+
_backButtonUseModernImplementation = newScreenProps.backButtonUseModernImplementation;
12191305

12201306
// We cannot compare SharedColor because it is shared value.
12211307
// We could compare color value, but it is more performant to just assign new value
@@ -1357,6 +1443,7 @@ - (RCTShadowView *)shadowView
13571443
RCT_EXPORT_VIEW_PROPERTY(backButtonInCustomView, BOOL)
13581444
RCT_EXPORT_VIEW_PROPERTY(disableBackButtonMenu, BOOL)
13591445
RCT_EXPORT_VIEW_PROPERTY(backButtonDisplayMode, UINavigationItemBackButtonDisplayMode)
1446+
RCT_EXPORT_VIEW_PROPERTY(backButtonUseModernImplementation, BOOL)
13601447
RCT_REMAP_VIEW_PROPERTY(hidden, hide, BOOL) // `hidden` is an UIView property, we need to use different name internally
13611448
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
13621449
RCT_EXPORT_VIEW_PROPERTY(headerLeftBarButtonItems, NSArray)

src/fabric/ScreenStackHeaderConfigNativeComponent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface NativeProps extends ViewProps {
7272
titleColor?: ColorValue;
7373
disableBackButtonMenu?: boolean;
7474
backButtonDisplayMode?: WithDefault<BackButtonDisplayMode, 'default'>;
75+
backButtonUseModernImplementation?: WithDefault<boolean, false>;
7576
hideBackButton?: boolean;
7677
backButtonInCustomView?: boolean;
7778
blurEffect?: WithDefault<BlurEffect, 'none'>;

src/types.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,12 @@ export interface ScreenStackHeaderConfigProps extends ViewProps {
603603
*/
604604
backTitleFontSize?: number;
605605
/**
606-
* @deprecated Setting this prop has no effect. Retained only for backward compatibility.
606+
* @deprecated This prop is respected only if `backButtonUseModernImplementation` is set to `false`.
607+
*
608+
* Whether the back button title should be visible or not. Defaults to `true`.
609+
*
610+
* When set to `false` it works as a "kill switch": it enforces `backButtonDisplayMode=minimal` and ignores `backButtonDisplayMode`, `backTitleFontSize`, `backTitleFontFamily`, `disableBackButtonMenu`.
611+
* For `backTitle` it works only in back button menu.
607612
*
608613
* @platform ios
609614
*/
@@ -648,9 +653,22 @@ export interface ScreenStackHeaderConfigProps extends ViewProps {
648653
* or `disableBackButtonMenu` property set is not supported due to limitations in the native platform.
649654
* In such cases, display mode will fallback to `default`.
650655
*
656+
* If `backButtonUseModernImplementation` is set to `false`, this prop is used only when none of: `backTitleFontFamily`,
657+
* `backTitleFontSize`, `disableBackButtonMenu` and `backTitleVisible=false` is set.
658+
*
651659
* @platform ios
652660
*/
653661
backButtonDisplayMode?: BackButtonDisplayMode;
662+
/**
663+
* Setting this prop to `true` is recommended. Prop available for backward compatibility.
664+
*
665+
* Uses new implementation for native back button configuration.
666+
*
667+
* @default false
668+
*
669+
* @platform ios
670+
*/
671+
backButtonUseModernImplementation?: boolean;
654672
/**
655673
* Array of UIBarButtomItems to the left side of the header.
656674
*

0 commit comments

Comments
 (0)