Skip to content

Commit 33267cf

Browse files
kligarskikkafar
authored andcommitted
feat(iOS, Tabs): set tab bar appearance via screen (#101)
Closes software-mansion/react-native-screens-labs#84. In this PR, we allow to pass (currently implemented) tab bar appearance props via `BottomTabsScreen`. These settings override configuration from `BottomTabs`. I also changed `badgeColor` to match other props. I did not change prop names to new convention with states yet (e.g. `tabBarNormalTitleFontSize`).
1 parent ecc6d3f commit 33267cf

14 files changed

+236
-58
lines changed

apps/src/tests/TestBottomTabs/BottomTabsContainer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export function BottomTabsContainer(props: BottomTabsContainerProps) {
8585
<BottomTabs
8686
onNativeFocusChange={onNativeFocusChangeCallback}
8787
tabBarBackgroundColor={Colors.NavyLight100}
88+
tabBarItemBadgeBackgroundColor={Colors.GreenDark100}
8889
experimentalControlNavigationStateInJS={
8990
configWrapper.config.controlledBottomTabs
9091
}>

apps/src/tests/TestBottomTabs/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const TAB_CONFIGS: TabConfiguration[] = [
1919
tabScreenProps: {
2020
tabKey: 'Tab1',
2121
badgeValue: '1',
22-
badgeColor: Colors.NavyDark80,
2322
title: 'Tab1',
2423
isFocused: true,
2524
},
@@ -29,7 +28,9 @@ const TAB_CONFIGS: TabConfiguration[] = [
2928
tabScreenProps: {
3029
tabKey: 'Tab2',
3130
badgeValue: '2',
32-
badgeColor: Colors.PurpleLight100,
31+
tabBarItemBadgeBackgroundColor: Colors.PurpleLight100,
32+
tabBarBackgroundColor: Colors.NavyDark140,
33+
tabBarItemTitleFontSize: 20,
3334
title: 'Tab2',
3435
},
3536
contentViewRenderFn: Tab2,
@@ -38,7 +39,7 @@ const TAB_CONFIGS: TabConfiguration[] = [
3839
tabScreenProps: {
3940
tabKey: 'Tab3',
4041
badgeValue: '3',
41-
badgeColor: Colors.YellowDark120,
42+
tabBarItemBadgeBackgroundColor: Colors.YellowDark120,
4243
title: 'Tab3',
4344
},
4445
contentViewRenderFn: Tab3,

ios/bottom-tabs/RNSBottomTabsHostComponentView.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#import "RNSEnums.h"
44
#import "RNSReactBaseView.h"
55
#import "RNSScreenContainer.h"
6+
#import "RNSTabBarAppearanceProvider.h"
67

78
NS_ASSUME_NONNULL_BEGIN
89

@@ -22,11 +23,12 @@ NS_ASSUME_NONNULL_BEGIN
2223

2324
#pragma mark - Props
2425

25-
@interface RNSBottomTabsHostComponentView ()
26+
@interface RNSBottomTabsHostComponentView () <RNSTabBarAppearanceProvider>
2627

2728
@property (nonatomic, strong, readonly, nullable) UIColor *tabBarBackgroundColor;
2829
@property (nonatomic, strong, readonly, nullable) UIBlurEffect *tabBarBlurEffect;
2930
@property (nonatomic, strong, readonly, nullable) NSNumber *tabBarItemTitleFontSize;
31+
@property (nonatomic, readonly, nullable) UIColor *tabBarItemBadgeBackgroundColor;
3032

3133
@property (nonatomic, readonly) BOOL experimental_controlNavigationStateInJS;
3234

ios/bottom-tabs/RNSBottomTabsHostComponentView.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ - (void)resetProps
6262
_tabBarBlurEffect = nil;
6363
_tabBarBackgroundColor = nil;
6464
_tabBarItemTitleFontSize = nil;
65+
_tabBarItemBadgeBackgroundColor = nil;
6566
}
6667

6768
#pragma mark - UIView methods
@@ -178,6 +179,11 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
178179
_tabBarItemTitleFontSize = [NSNumber numberWithFloat:newComponentProps.tabBarItemTitleFontSize];
179180
_needsTabBarAppearanceUpdate = YES;
180181
}
182+
183+
if (newComponentProps.tabBarItemBadgeBackgroundColor != oldComponentProps.tabBarItemBadgeBackgroundColor) {
184+
_tabBarItemBadgeBackgroundColor = RCTUIColorFromSharedColor(newComponentProps.tabBarItemBadgeBackgroundColor);
185+
_needsTabBarAppearanceUpdate = YES;
186+
}
181187

182188
// Super call updates _props pointer. We should NOT update it before calling super.
183189
[super updateProps:props oldProps:oldProps];

ios/bottom-tabs/RNSBottomTabsScreenComponentView.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#import "RNSBottomTabsScreenEventEmitter.h"
2+
#import "RNSEnums.h"
23
#import "RNSReactBaseView.h"
4+
#import "RNSTabBarAppearanceProvider.h"
35

46
NS_ASSUME_NONNULL_BEGIN
57

@@ -29,15 +31,18 @@ NS_ASSUME_NONNULL_BEGIN
2931
/**
3032
* Properties set on component in JavaScript.
3133
*/
32-
@interface RNSBottomTabsScreenComponentView ()
34+
@interface RNSBottomTabsScreenComponentView () <RNSTabBarAppearanceProvider>
3335

3436
@property (nonatomic, readonly) BOOL isSelectedScreen;
3537
@property (nonatomic, readonly, nullable) NSString *tabKey;
3638
@property (nonatomic, readonly, nullable) NSString *badgeValue;
37-
@property (nonatomic, readonly, nullable) UIColor *badgeColor;
39+
40+
@property (nonatomic, strong, readonly, nullable) UIColor *tabBarBackgroundColor;
41+
@property (nonatomic, strong, readonly, nullable) UIBlurEffect *tabBarBlurEffect;
42+
@property (nonatomic, strong, readonly, nullable) NSNumber *tabBarItemTitleFontSize;
43+
@property (nonatomic, readonly, nullable) UIColor *tabBarItemBadgeBackgroundColor;
3844

3945
@property (nonatomic, readonly, nullable) NSString *title;
40-
@property (nonatomic, readonly, nullable) NSNumber *titleFontSize;
4146

4247
@end
4348

ios/bottom-tabs/RNSBottomTabsScreenComponentView.mm

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import "RNSBottomTabsScreenComponentView.h"
22
#import "RNSDefines.h"
33
#import "RNSTabBarController.h"
4+
#import "RNSConversions.h"
45

56
#import <React/RCTConversions.h>
67
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
@@ -46,7 +47,10 @@ - (void)resetProps
4647
_isSelectedScreen = NO;
4748
_badgeValue = nil;
4849
_title = nil;
49-
_badgeColor = nil;
50+
_tabBarBlurEffect = nil;
51+
_tabBarBackgroundColor = nil;
52+
_tabBarItemTitleFontSize = nil;
53+
_tabBarItemBadgeBackgroundColor = nil;
5054
}
5155

5256
RNS_IGNORE_SUPER_CALL_BEGIN
@@ -89,11 +93,6 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
8993
_tabKey = RCTNSStringFromString(newComponentProps.title);
9094
}
9195

92-
if (newComponentProps.titleFontSize != oldComponentProps.titleFontSize) {
93-
_titleFontSize = [NSNumber numberWithFloat:newComponentProps.titleFontSize];
94-
tabItemNeedsAppearanceUpdate = true;
95-
}
96-
9796
if (newComponentProps.isFocused != oldComponentProps.isFocused) {
9897
_isSelectedScreen = newComponentProps.isFocused;
9998
[_controller tabScreenFocusHasChanged];
@@ -104,12 +103,28 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
104103
_controller.tabBarItem.badgeValue = _badgeValue;
105104
}
106105

107-
if (newComponentProps.badgeColor != oldComponentProps.badgeColor) {
108-
_badgeColor = RCTUIColorFromSharedColor(newComponentProps.badgeColor);
106+
if (newComponentProps.tabBarItemBadgeBackgroundColor != oldComponentProps.tabBarItemBadgeBackgroundColor) {
107+
_tabBarItemBadgeBackgroundColor = RCTUIColorFromSharedColor(newComponentProps.tabBarItemBadgeBackgroundColor);
109108
// Note that this will prevent default color from being set.
110109
// TODO: support default color by setting nil here.
111-
NSLog(@"TabsScreen [%ld] update badgeColor to %@", self.tag, _badgeColor);
112-
tabItemNeedsAppearanceUpdate = true;
110+
NSLog(@"TabsScreen [%ld] update badgeColor to %@", self.tag, _tabBarItemBadgeBackgroundColor);
111+
tabItemNeedsAppearanceUpdate = YES;
112+
}
113+
114+
if (newComponentProps.tabBarBackgroundColor != oldComponentProps.tabBarBackgroundColor) {
115+
_tabBarBackgroundColor = RCTUIColorFromSharedColor(newComponentProps.tabBarBackgroundColor);
116+
tabItemNeedsAppearanceUpdate = YES;
117+
}
118+
119+
if (newComponentProps.tabBarBlurEffect != oldComponentProps.tabBarBlurEffect) {
120+
_tabBarBlurEffect =
121+
rnscreens::conversion::RNSUIBlurEffectFromRNSBottomTabsScreenTabBarBlurEffect(newComponentProps.tabBarBlurEffect);
122+
tabItemNeedsAppearanceUpdate = YES;
123+
}
124+
125+
if (newComponentProps.tabBarItemTitleFontSize != oldComponentProps.tabBarItemTitleFontSize) {
126+
_tabBarItemTitleFontSize = [NSNumber numberWithFloat:newComponentProps.tabBarItemTitleFontSize];
127+
tabItemNeedsAppearanceUpdate = YES;
113128
}
114129

115130
if (tabItemNeedsAppearanceUpdate) {

ios/bottom-tabs/RNSTabBarAppearanceCoordinator.mm

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import "RNSTabBarAppearanceCoordinator.h"
22
#import <React/RCTFont.h>
33
#import "RNSConversions.h"
4+
#import "RNSTabBarAppearanceProvider.h"
45
#import "RNSTabsScreenViewController.h"
56

67
@implementation RNSTabBarAppearanceCoordinator
@@ -18,8 +19,7 @@ - (void)updateAppearanceOfTabBar:(nullable UITabBar *)tabBar
1819

1920
// Step 2 - general settings
2021
if (hostComponentView != nil) {
21-
appearance.backgroundColor = hostComponentView.tabBarBackgroundColor;
22-
appearance.backgroundEffect = hostComponentView.tabBarBlurEffect;
22+
[self configureTabBarAppearance:appearance fromAppearanceProvider:hostComponentView];
2323

2424
[self configureTabBarItemAppearance:appearance.stackedLayoutAppearance withTabsHost:hostComponentView];
2525
[self configureTabBarItemAppearance:appearance.compactInlineLayoutAppearance withTabsHost:hostComponentView];
@@ -45,6 +45,8 @@ - (void)updateAppearanceOfTabBar:(nullable UITabBar *)tabBar
4545
// Inherit general properties from host
4646
UITabBarAppearance *tabAppearance = [[UITabBarAppearance alloc] initWithBarAppearance:appearance];
4747

48+
[self configureTabBarAppearance:tabAppearance fromAppearanceProvider:tabScreenCtrl.tabScreenComponentView];
49+
4850
[self configureTabBarItemAppearance:tabAppearance.compactInlineLayoutAppearance
4951
forTabScreenController:tabScreenCtrl
5052
withHostComponentView:hostComponentView];
@@ -55,11 +57,31 @@ - (void)updateAppearanceOfTabBar:(nullable UITabBar *)tabBar
5557
forTabScreenController:tabScreenCtrl
5658
withHostComponentView:hostComponentView];
5759

58-
tabScreenCtrl.tabBarItem.standardAppearance = tabAppearance;
59-
tabScreenCtrl.tabBarItem.scrollEdgeAppearance = tabAppearance;
60+
[self configureTabBarItemForTabScreenController:tabScreenCtrl withAppearace:tabAppearance];
61+
}
62+
}
63+
64+
- (void)configureTabBarAppearance:(nonnull UITabBarAppearance *)appearance
65+
fromAppearanceProvider:(id<RNSTabBarAppearanceProvider>)appearanceProvider
66+
{
67+
if (appearanceProvider.tabBarBackgroundColor != nil) {
68+
appearance.backgroundColor = appearanceProvider.tabBarBackgroundColor;
69+
}
70+
71+
if (appearanceProvider.tabBarBlurEffect != nil) {
72+
appearance.backgroundEffect = appearanceProvider.tabBarBlurEffect;
6073
}
6174
}
6275

76+
- (void)configureTabBarItemForTabScreenController:(nonnull RNSTabsScreenViewController *)tabScreenCtrl
77+
withAppearace:(nonnull UITabBarAppearance *)tabAppearance
78+
{
79+
UITabBarItem *tabBarItem = tabScreenCtrl.tabBarItem;
80+
81+
tabBarItem.standardAppearance = tabAppearance;
82+
tabBarItem.scrollEdgeAppearance = tabAppearance;
83+
}
84+
6385
- (void)configureTabBarItemAppearance:(nonnull UITabBarItemAppearance *)tabBarItemAppearance
6486
withTabsHost:(nonnull RNSBottomTabsHostComponentView *)hostComponent
6587
{
@@ -77,16 +99,16 @@ - (void)configureTabBarItemAppearance:(nonnull UITabBarItemAppearance *)tabBarIt
7799
}
78100

79101
[self configureTabBarItemStateAppearance:tabBarItemAppearance.normal
80-
withTabsHost:hostComponent
102+
withAppearanceProvider:hostComponent
81103
withTitleTextAttributes:titleTextAttributes];
82104
[self configureTabBarItemStateAppearance:tabBarItemAppearance.selected
83-
withTabsHost:hostComponent
105+
withAppearanceProvider:hostComponent
84106
withTitleTextAttributes:titleTextAttributes];
85107
[self configureTabBarItemStateAppearance:tabBarItemAppearance.focused
86-
withTabsHost:hostComponent
108+
withAppearanceProvider:hostComponent
87109
withTitleTextAttributes:titleTextAttributes];
88110
[self configureTabBarItemStateAppearance:tabBarItemAppearance.disabled
89-
withTabsHost:hostComponent
111+
withAppearanceProvider:hostComponent
90112
withTitleTextAttributes:titleTextAttributes];
91113
}
92114

@@ -96,46 +118,40 @@ - (void)configureTabBarItemAppearance:(nonnull UITabBarItemAppearance *)tabBarIt
96118
{
97119
NSMutableDictionary *titleTextAttributes = nil;
98120

99-
if (tabScreenCtrl.tabScreenComponentView.titleFontSize != nil) {
121+
if (tabScreenCtrl.tabScreenComponentView.tabBarItemTitleFontSize != nil) {
100122
titleTextAttributes = [[NSMutableDictionary alloc] init];
101-
titleTextAttributes[NSFontAttributeName] = [RCTFont updateFont:nil
102-
withFamily:nil
103-
size:tabScreenCtrl.tabScreenComponentView.titleFontSize
104-
weight:nil
105-
style:nil
106-
variant:nil
107-
scaleMultiplier:1.0];
123+
titleTextAttributes[NSFontAttributeName] =
124+
[RCTFont updateFont:nil
125+
withFamily:nil
126+
size:tabScreenCtrl.tabScreenComponentView.tabBarItemTitleFontSize
127+
weight:nil
128+
style:nil
129+
variant:nil
130+
scaleMultiplier:1.0];
108131
}
109132

110133
[self configureTabBarItemStateAppearance:tabBarItemAppearance.normal
111-
forTabScreenController:tabScreenCtrl
134+
withAppearanceProvider:tabScreenCtrl.tabScreenComponentView
112135
withTitleTextAttributes:titleTextAttributes];
113136
[self configureTabBarItemStateAppearance:tabBarItemAppearance.selected
114-
forTabScreenController:tabScreenCtrl
137+
withAppearanceProvider:tabScreenCtrl.tabScreenComponentView
115138
withTitleTextAttributes:titleTextAttributes];
116139
[self configureTabBarItemStateAppearance:tabBarItemAppearance.focused
117-
forTabScreenController:tabScreenCtrl
140+
withAppearanceProvider:tabScreenCtrl.tabScreenComponentView
118141
withTitleTextAttributes:titleTextAttributes];
119142
[self configureTabBarItemStateAppearance:tabBarItemAppearance.disabled
120-
forTabScreenController:tabScreenCtrl
143+
withAppearanceProvider:tabScreenCtrl.tabScreenComponentView
121144
withTitleTextAttributes:titleTextAttributes];
122145
}
123146

124147
- (void)configureTabBarItemStateAppearance:(nonnull UITabBarItemStateAppearance *)tabBarItemStateAppearance
125-
forTabScreenController:(nonnull RNSTabsScreenViewController *)tabScreenCtrl
148+
withAppearanceProvider:(id<RNSTabBarAppearanceProvider>)appearanceProvider
126149
withTitleTextAttributes:(nullable NSDictionary<NSAttributedStringKey, id> *)titleTextAttributes
127150
{
128-
tabBarItemStateAppearance.badgeBackgroundColor = tabScreenCtrl.tabScreenComponentView.badgeColor;
129-
130-
if (titleTextAttributes != nil) {
131-
tabBarItemStateAppearance.titleTextAttributes = titleTextAttributes;
151+
if (appearanceProvider.tabBarItemBadgeBackgroundColor != nil) {
152+
tabBarItemStateAppearance.badgeBackgroundColor = appearanceProvider.tabBarItemBadgeBackgroundColor;
132153
}
133-
}
134154

135-
- (void)configureTabBarItemStateAppearance:(nonnull UITabBarItemStateAppearance *)tabBarItemStateAppearance
136-
withTabsHost:(nonnull RNSBottomTabsHostComponentView *)hostComponent
137-
withTitleTextAttributes:(nullable NSDictionary<NSAttributedStringKey, id> *)titleTextAttributes
138-
{
139155
if (titleTextAttributes != nil) {
140156
tabBarItemStateAppearance.titleTextAttributes = titleTextAttributes;
141157
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#import <UIKit/UIKit.h>
2+
3+
NS_ASSUME_NONNULL_BEGIN
4+
5+
@protocol RNSTabBarAppearanceProvider
6+
7+
-(UIColor *)tabBarBackgroundColor;
8+
-(UIBlurEffect *)tabBarBlurEffect;
9+
-(NSNumber *)tabBarItemTitleFontSize;
10+
-(UIColor *)tabBarItemBadgeBackgroundColor;
11+
12+
@end
13+
14+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)