|
1 | 1 | #import "RNNTabBarItemCreator.h" |
2 | 2 | #import "RNNFontAttributesCreator.h" |
3 | 3 | #import "UIImage+utils.h" |
| 4 | +#import <React/RCTLog.h> |
4 | 5 |
|
5 | 6 | @implementation RNNTabBarItemCreator |
6 | 7 |
|
| 8 | ++ (NSNumber *)systemItemForRole:(NSString *)role { |
| 9 | + static NSDictionary<NSString *, NSNumber *> *map = nil; |
| 10 | + static dispatch_once_t onceToken; |
| 11 | + dispatch_once(&onceToken, ^{ |
| 12 | + map = @{ |
| 13 | + @"search" : @(UITabBarSystemItemSearch), |
| 14 | + @"bookmarks" : @(UITabBarSystemItemBookmarks), |
| 15 | + @"contacts" : @(UITabBarSystemItemContacts), |
| 16 | + @"downloads" : @(UITabBarSystemItemDownloads), |
| 17 | + @"favorites" : @(UITabBarSystemItemFavorites), |
| 18 | + @"featured" : @(UITabBarSystemItemFeatured), |
| 19 | + @"history" : @(UITabBarSystemItemHistory), |
| 20 | + @"more" : @(UITabBarSystemItemMore), |
| 21 | + @"mostRecent" : @(UITabBarSystemItemMostRecent), |
| 22 | + @"mostViewed" : @(UITabBarSystemItemMostViewed), |
| 23 | + @"recents" : @(UITabBarSystemItemRecents), |
| 24 | + @"topRated" : @(UITabBarSystemItemTopRated), |
| 25 | + }; |
| 26 | + }); |
| 27 | + return map[role]; |
| 28 | +} |
| 29 | + |
7 | 30 | - (UITabBarItem *)createTabBarItem:(UITabBarItem *)mergeItem { |
8 | 31 | return mergeItem ?: [UITabBarItem new]; |
9 | 32 | } |
10 | 33 |
|
11 | 34 | - (UITabBarItem *)createTabBarItem:(RNNBottomTabOptions *)bottomTabOptions |
12 | 35 | mergeItem:(UITabBarItem *)mergeItem { |
| 36 | + |
| 37 | + if (bottomTabOptions.role.hasValue) { |
| 38 | + UITabBarItem *roleItem = [self createSystemItemForRole:bottomTabOptions |
| 39 | + mergeItem:mergeItem]; |
| 40 | + if (roleItem) |
| 41 | + return roleItem; |
| 42 | + } |
| 43 | + |
13 | 44 | UITabBarItem *tabItem = [self createTabBarItem:mergeItem]; |
14 | 45 | UIImage *icon = [bottomTabOptions.icon withDefault:nil]; |
15 | 46 | UIImage *selectedIcon = [bottomTabOptions.selectedIcon withDefault:icon]; |
@@ -55,6 +86,71 @@ - (UITabBarItem *)createTabBarItem:(RNNBottomTabOptions *)bottomTabOptions |
55 | 86 | return tabItem; |
56 | 87 | } |
57 | 88 |
|
| 89 | +#pragma mark - Role (system item) creation |
| 90 | + |
| 91 | +- (UITabBarItem *)createSystemItemForRole:(RNNBottomTabOptions *)bottomTabOptions |
| 92 | + mergeItem:(UITabBarItem *)mergeItem { |
| 93 | + NSString *role = [bottomTabOptions.role withDefault:nil]; |
| 94 | + NSNumber *systemItemNumber = [RNNTabBarItemCreator systemItemForRole:role]; |
| 95 | + |
| 96 | + if (!systemItemNumber) { |
| 97 | + RCTLogWarn(@"[RNN] Unknown bottomTab role '%@' — falling back to normal tab.", role); |
| 98 | + return nil; |
| 99 | + } |
| 100 | + |
| 101 | + if ([role isEqualToString:@"search"] && self.searchRoleUsed) { |
| 102 | + RCTLogWarn(@"[RNN] Only one tab per bottomTabs layout may use role:'search'. " |
| 103 | + @"Subsequent 'search' roles are ignored — creating a normal tab instead."); |
| 104 | + return nil; |
| 105 | + } |
| 106 | + |
| 107 | + if ([role isEqualToString:@"search"]) { |
| 108 | + self.searchRoleUsed = YES; |
| 109 | + } |
| 110 | + |
| 111 | + UITabBarItem *tabItem = |
| 112 | + [[UITabBarItem alloc] initWithTabBarSystemItem:(UITabBarSystemItem)[systemItemNumber integerValue] |
| 113 | + tag:bottomTabOptions.tag]; |
| 114 | + |
| 115 | + UITabBarItem *baseItem = [self createTabBarItem:mergeItem]; |
| 116 | + tabItem.standardAppearance = baseItem.standardAppearance; |
| 117 | +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 |
| 118 | + if (@available(iOS 15.0, *)) { |
| 119 | + tabItem.scrollEdgeAppearance = baseItem.scrollEdgeAppearance; |
| 120 | + } |
| 121 | +#endif |
| 122 | + |
| 123 | + UIImage *icon = [bottomTabOptions.icon withDefault:nil]; |
| 124 | + UIImage *selectedIcon = [bottomTabOptions.selectedIcon withDefault:nil]; |
| 125 | + UIColor *iconColor = [bottomTabOptions.iconColor withDefault:nil]; |
| 126 | + UIColor *selectedIconColor = [bottomTabOptions.selectedIconColor withDefault:iconColor]; |
| 127 | + |
| 128 | + if (@available(iOS 13.0, *)) { |
| 129 | + if (bottomTabOptions.sfSymbol.hasValue) { |
| 130 | + icon = [UIImage systemImageNamed:[bottomTabOptions.sfSymbol withDefault:nil]]; |
| 131 | + } |
| 132 | + if (bottomTabOptions.sfSelectedSymbol.hasValue) { |
| 133 | + selectedIcon = [UIImage systemImageNamed:[bottomTabOptions.sfSelectedSymbol withDefault:nil]]; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + if (icon) { |
| 138 | + tabItem.image = [self getIconImage:icon withTint:iconColor]; |
| 139 | + } |
| 140 | + if (selectedIcon) { |
| 141 | + tabItem.selectedImage = [self getSelectedIconImage:selectedIcon |
| 142 | + selectedIconColor:selectedIconColor]; |
| 143 | + } |
| 144 | + |
| 145 | + tabItem.accessibilityIdentifier = [bottomTabOptions.testID withDefault:nil]; |
| 146 | + tabItem.accessibilityLabel = [bottomTabOptions.accessibilityLabel withDefault:nil]; |
| 147 | + [self appendTitleAttributes:tabItem bottomTabOptions:bottomTabOptions]; |
| 148 | + |
| 149 | + return tabItem; |
| 150 | +} |
| 151 | + |
| 152 | +#pragma mark - Icon helpers |
| 153 | + |
58 | 154 | - (UIImage *)getSelectedIconImage:(UIImage *)selectedIcon |
59 | 155 | selectedIconColor:(UIColor *)selectedIconColor { |
60 | 156 | if (selectedIcon) { |
@@ -82,6 +178,8 @@ - (UIImage *)getIconImage:(UIImage *)icon withTint:(UIColor *)tintColor { |
82 | 178 | return nil; |
83 | 179 | } |
84 | 180 |
|
| 181 | +#pragma mark - Title attributes |
| 182 | + |
85 | 183 | - (void)appendTitleAttributes:(UITabBarItem *)tabItem |
86 | 184 | bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions { |
87 | 185 | UIColor *textColor = [bottomTabOptions.textColor withDefault:[UIColor blackColor]]; |
|
0 commit comments