Skip to content

Commit 629df83

Browse files
authored
fix(iOS, Tabs): update tab bar item only when necessary (#3290)
## Description Limits changing tab bar item instances ([PR introducing this](#3164)) to only necessary cases (changing `systemItem`). Unnecessary updates caused a bug when changing e.g. only `badgeValue`. | before | after | | --- | --- | | <video src="https://github.com/user-attachments/assets/d99e1120-8311-48d5-8d69-b7079c328ddd" /> | <video src="https://github.com/user-attachments/assets/444a60aa-e576-42e3-ba32-2b4232c0234c" /> | Fixes #3273. ## Changes - separate creating new `UITabBarItem` instance and updating tab bar item related props - create new `UITabBarItem` instance only on `systemItem` change - restore default system item's icon by extracting it from new `UITabBarSystemItem` instance instead of changing the entire `UITabBarItem` ## Test code and steps to reproduce Run `TestBottomTabs`. Change `badgeValue` on selected tab. You can also check test cases from #3164. ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
1 parent 1b3d3f9 commit 629df83

File tree

2 files changed

+41
-16
lines changed

2 files changed

+41
-16
lines changed

ios/bottom-tabs/RNSBottomTabsScreenComponentView.mm

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ @implementation RNSBottomTabsScreenComponentView {
3535
#if !RCT_NEW_ARCH_ENABLED
3636
BOOL _tabItemNeedsAppearanceUpdate;
3737
BOOL _tabScreenOrientationNeedsUpdate;
38+
BOOL _tabBarItemNeedsRecreation;
3839
BOOL _tabBarItemNeedsUpdate;
3940
BOOL _scrollEdgeEffectsNeedUpdate;
4041
#endif // !RCT_NEW_ARCH_ENABLED
@@ -64,6 +65,7 @@ - (void)initState
6465
#if !RCT_NEW_ARCH_ENABLED
6566
_tabItemNeedsAppearanceUpdate = NO;
6667
_tabScreenOrientationNeedsUpdate = NO;
68+
_tabBarItemNeedsRecreation = NO;
6769
_tabBarItemNeedsUpdate = NO;
6870
_scrollEdgeEffectsNeedUpdate = NO;
6971
#endif
@@ -172,7 +174,7 @@ - (void)updateContentScrollViewEdgeEffectsIfExists
172174

173175
#pragma mark - Prop update utils
174176

175-
- (void)updateTabBarItem
177+
- (void)createTabBarItem
176178
{
177179
UITabBarItem *tabBarItem = nil;
178180
if (_systemItem != RNSBottomTabsScreenSystemItemNone) {
@@ -181,14 +183,24 @@ - (void)updateTabBarItem
181183
tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:0];
182184
} else {
183185
tabBarItem = [[UITabBarItem alloc] init];
184-
tabBarItem.title = _title;
185186
}
186187

187-
tabBarItem.badgeValue = _badgeValue;
188-
189188
_controller.tabBarItem = tabBarItem;
190189
}
191190

191+
- (void)updateTabBarItem
192+
{
193+
UITabBarItem *tabBarItem = _controller.tabBarItem;
194+
195+
if (![tabBarItem.title isEqualToString:_title]) {
196+
tabBarItem.title = _title;
197+
}
198+
199+
if (![tabBarItem.badgeValue isEqualToString:_badgeValue]) {
200+
tabBarItem.badgeValue = _badgeValue;
201+
}
202+
}
203+
192204
#pragma mark - RNSSafeAreaProviding
193205

194206
- (UIEdgeInsets)providerSafeAreaInsets
@@ -222,6 +234,7 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
222234

223235
bool tabItemNeedsAppearanceUpdate{false};
224236
bool tabScreenOrientationNeedsUpdate{false};
237+
bool tabBarItemNeedsRecreation{false};
225238
bool tabBarItemNeedsUpdate{false};
226239
bool scrollEdgeEffectsNeedUpdate{false};
227240

@@ -274,33 +287,28 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
274287
if (newComponentProps.iconType != oldComponentProps.iconType) {
275288
_iconType = rnscreens::conversion::RNSBottomTabsIconTypeFromIcon(newComponentProps.iconType);
276289
tabItemNeedsAppearanceUpdate = YES;
277-
tabBarItemNeedsUpdate = YES;
278290
}
279291

280292
if (newComponentProps.iconImageSource != oldComponentProps.iconImageSource) {
281293
_iconImageSource =
282294
rnscreens::conversion::RCTImageSourceFromImageSourceAndIconType(&newComponentProps.iconImageSource, _iconType);
283295
tabItemNeedsAppearanceUpdate = YES;
284-
tabBarItemNeedsUpdate = YES;
285296
}
286297

287298
if (newComponentProps.iconSfSymbolName != oldComponentProps.iconSfSymbolName) {
288299
_iconSfSymbolName = RCTNSStringFromStringNilIfEmpty(newComponentProps.iconSfSymbolName);
289300
tabItemNeedsAppearanceUpdate = YES;
290-
tabBarItemNeedsUpdate = YES;
291301
}
292302

293303
if (newComponentProps.selectedIconImageSource != oldComponentProps.selectedIconImageSource) {
294304
_selectedIconImageSource = rnscreens::conversion::RCTImageSourceFromImageSourceAndIconType(
295305
&newComponentProps.selectedIconImageSource, _iconType);
296306
tabItemNeedsAppearanceUpdate = YES;
297-
tabBarItemNeedsUpdate = YES;
298307
}
299308

300309
if (newComponentProps.selectedIconSfSymbolName != oldComponentProps.selectedIconSfSymbolName) {
301310
_selectedIconSfSymbolName = RCTNSStringFromStringNilIfEmpty(newComponentProps.selectedIconSfSymbolName);
302311
tabItemNeedsAppearanceUpdate = YES;
303-
tabBarItemNeedsUpdate = YES;
304312
}
305313

306314
if (newComponentProps.specialEffects.repeatedTabSelection.popToRoot !=
@@ -334,7 +342,7 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
334342
if (newComponentProps.systemItem != oldComponentProps.systemItem) {
335343
_systemItem = rnscreens::conversion::RNSBottomTabsScreenSystemItemFromReactRNSBottomTabsScreenSystemItem(
336344
newComponentProps.systemItem);
337-
tabBarItemNeedsUpdate = YES;
345+
tabBarItemNeedsRecreation = YES;
338346
}
339347

340348
if (newComponentProps.bottomScrollEdgeEffect != oldComponentProps.bottomScrollEdgeEffect) {
@@ -367,6 +375,11 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
367375
scrollEdgeEffectsNeedUpdate = YES;
368376
}
369377

378+
if (tabBarItemNeedsRecreation) {
379+
[self createTabBarItem];
380+
tabBarItemNeedsUpdate = YES;
381+
}
382+
370383
if (tabBarItemNeedsUpdate) {
371384
[self updateTabBarItem];
372385

@@ -445,6 +458,13 @@ - (void)didSetProps:(NSArray<NSString *> *)changedProps
445458
// didSetProps will always be called because tabKey prop is required.
446459
_isOverrideScrollViewContentInsetAdjustmentBehaviorSet = YES;
447460

461+
if (_tabBarItemNeedsRecreation) {
462+
[self createTabBarItem];
463+
_tabBarItemNeedsRecreation = NO;
464+
465+
_tabBarItemNeedsUpdate = YES;
466+
}
467+
448468
if (_tabBarItemNeedsUpdate) {
449469
[self updateTabBarItem];
450470
_tabBarItemNeedsUpdate = NO;
@@ -501,35 +521,30 @@ - (void)setIconType:(RNSBottomTabsIconType)iconType
501521
{
502522
_iconType = iconType;
503523
_tabItemNeedsAppearanceUpdate = YES;
504-
_tabBarItemNeedsUpdate = YES;
505524
}
506525

507526
- (void)setIconImageSource:(RCTImageSource *)iconImageSource
508527
{
509528
_iconImageSource = iconImageSource;
510529
_tabItemNeedsAppearanceUpdate = YES;
511-
_tabBarItemNeedsUpdate = YES;
512530
}
513531

514532
- (void)setIconSfSymbolName:(NSString *)iconSfSymbolName
515533
{
516534
_iconSfSymbolName = [NSString rnscreens_stringOrNilIfEmpty:iconSfSymbolName];
517535
_tabItemNeedsAppearanceUpdate = YES;
518-
_tabBarItemNeedsUpdate = YES;
519536
}
520537

521538
- (void)setSelectedIconImageSource:(RCTImageSource *)selectedIconImageSource
522539
{
523540
_selectedIconImageSource = selectedIconImageSource;
524541
_tabItemNeedsAppearanceUpdate = YES;
525-
_tabBarItemNeedsUpdate = YES;
526542
}
527543

528544
- (void)setSelectedIconSfSymbolName:(NSString *)selectedIconSfSymbolName
529545
{
530546
_selectedIconSfSymbolName = [NSString rnscreens_stringOrNilIfEmpty:selectedIconSfSymbolName];
531547
_tabItemNeedsAppearanceUpdate = YES;
532-
_tabBarItemNeedsUpdate = YES;
533548
}
534549

535550
- (void)setBottomScrollEdgeEffect:(RNSScrollEdgeEffect)bottomScrollEdgeEffect
@@ -596,7 +611,7 @@ - (void)setScrollEdgeAppearance:(NSDictionary *)scrollEdgeAppearanceProps
596611
- (void)setSystemItem:(RNSBottomTabsScreenSystemItem)systemItem
597612
{
598613
_systemItem = systemItem;
599-
_tabBarItemNeedsUpdate = YES;
614+
_tabBarItemNeedsRecreation = YES;
600615
}
601616

602617
- (void)setOrientation:(RNSOrientation)orientation

ios/bottom-tabs/RNSTabBarAppearanceCoordinator.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,20 @@ - (void)setIconsForTabBarItem:(UITabBarItem *)tabBarItem
5555
if (screenView.iconType == RNSBottomTabsIconTypeSfSymbol) {
5656
if (screenView.iconSfSymbolName != nil) {
5757
tabBarItem.image = [UIImage systemImageNamed:screenView.iconSfSymbolName];
58+
} else if (screenView.systemItem != RNSBottomTabsScreenSystemItemNone) {
59+
// Restore default system item icon
60+
UITabBarSystemItem systemItem =
61+
rnscreens::conversion::RNSBottomTabsScreenSystemItemToUITabBarSystemItem(screenView.systemItem);
62+
tabBarItem.image = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:0].image;
5863
}
5964

6065
if (screenView.selectedIconSfSymbolName != nil) {
6166
tabBarItem.selectedImage = [UIImage systemImageNamed:screenView.selectedIconSfSymbolName];
67+
} else if (screenView.systemItem != RNSBottomTabsScreenSystemItemNone) {
68+
// Restore default system item icon
69+
UITabBarSystemItem systemItem =
70+
rnscreens::conversion::RNSBottomTabsScreenSystemItemToUITabBarSystemItem(screenView.systemItem);
71+
tabBarItem.selectedImage = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:0].selectedImage;
6272
}
6373
} else if (imageLoader != nil) {
6474
bool isTemplate = screenView.iconType == RNSBottomTabsIconTypeTemplate;

0 commit comments

Comments
 (0)