Skip to content

Commit 0792ae1

Browse files
committed
fix(iOS): icons not following inactiveTintColor
1 parent f2e12e7 commit 0792ae1

6 files changed

Lines changed: 93 additions & 7 deletions

File tree

packages/react-native-bottom-tabs/ios/TabItem.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,36 @@ struct TabItem: View {
55
var icon: PlatformImage?
66
var sfSymbol: String?
77
var labeled: Bool?
8+
var tintColor: PlatformColor?
89

10+
private var tint: Color? {
11+
tintColor.map(Color.init)
12+
}
13+
14+
#if !os(macOS)
15+
private var tintedIcon: PlatformImage? {
16+
guard let icon else { return nil }
17+
guard let tintColor else { return icon }
18+
return icon.withTintColor(tintColor, renderingMode: .alwaysOriginal)
19+
}
20+
#endif
21+
22+
@ViewBuilder
923
var body: some View {
1024
if let icon {
1125
#if os(macOS)
1226
Image(nsImage: icon)
1327
#else
14-
Image(uiImage: icon)
28+
Image(uiImage: tintedIcon ?? icon)
1529
#endif
1630
} else if let sfSymbol, !sfSymbol.isEmpty {
1731
Image(systemName: sfSymbol)
1832
.noneSymbolVariant()
33+
.foregroundColor(tint)
1934
}
2035
if labeled != false {
2136
Text(title ?? "")
37+
.foregroundColor(tint)
2238
}
2339
}
2440
}

packages/react-native-bottom-tabs/ios/TabView/LegacyTabView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct LegacyTabView: AnyTabView {
4040

4141
if !tabData.hidden || isFocused {
4242
let icon = props.icons[index]
43+
let tintColor = isFocused ? tabData.activeTintColor ?? props.activeTintColor : props.inactiveTintColor
4344
let child = props.children[safe: index]?.view ?? PlatformView()
4445
let context = TabAppearContext(
4546
index: index,
@@ -56,7 +57,8 @@ struct LegacyTabView: AnyTabView {
5657
title: tabData.title,
5758
icon: icon,
5859
sfSymbol: tabData.sfSymbol,
59-
labeled: props.labeled
60+
labeled: props.labeled,
61+
tintColor: tintColor
6062
)
6163
.accessibilityIdentifier(tabData.testID ?? "")
6264
}

packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct NewTabView: AnyTabView {
2929

3030
if !tabData.hidden || isFocused {
3131
let icon = props.icons[index]
32+
let tintColor = isFocused ? tabData.activeTintColor ?? props.activeTintColor : props.inactiveTintColor
3233

3334
let context = TabAppearContext(
3435
index: index,
@@ -48,7 +49,8 @@ struct NewTabView: AnyTabView {
4849
title: tabData.title,
4950
icon: icon,
5051
sfSymbol: tabData.sfSymbol,
51-
labeled: props.labeled
52+
labeled: props.labeled,
53+
tintColor: tintColor
5254
)
5355
}
5456
#if !os(tvOS)

packages/react-native-bottom-tabs/ios/TabViewImpl.swift

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct TabViewImpl: View {
6969
tabBar = tabController
7070
#else
7171
tabBar = tabController.tabBar
72+
updateTabBarAppearance(props: props, tabBar: tabController.tabBar)
7273
if !props.tabBarHidden {
7374
onTabBarMeasured(
7475
Int(tabController.tabBar.frame.size.height)
@@ -82,6 +83,9 @@ struct TabViewImpl: View {
8283
.tintColor(props.selectedActiveTintColor)
8384
.getSidebarAdaptable(enabled: props.sidebarAdaptable ?? false)
8485
.onChange(of: props.selectedPage ?? "") { newValue in
86+
#if !os(macOS)
87+
updateTabBarAppearance(props: props, tabBar: tabBar)
88+
#endif
8589
#if !os(macOS)
8690
if props.disablePageAnimations {
8791
UIView.setAnimationsEnabled(false)
@@ -120,36 +124,41 @@ struct TabViewImpl: View {
120124
if props.scrollEdgeAppearance == "transparent" {
121125
configureTransparentAppearance(tabBar: tabBar, props: props)
122126
return
127+
} else {
128+
configureStandardAppearance(tabBar: tabBar, props: props)
123129
}
124-
125-
configureStandardAppearance(tabBar: tabBar, props: props)
126130
}
127131
#endif
128132

129133
#if !os(macOS)
130134
private func configureTransparentAppearance(tabBar: UITabBar, props: TabViewProps) {
131135
tabBar.barTintColor = props.barTintColor
136+
tabBar.tintColor = props.selectedActiveTintColor
132137
#if !os(visionOS)
133138
tabBar.isTranslucent = props.translucent
134139
#endif
135140
tabBar.unselectedItemTintColor = props.inactiveTintColor
136141

137142
guard let items = tabBar.items else { return }
143+
configureTabBarItemImages(items: items, props: props)
138144

139145
let attributes = TabBarFontSize.createNormalStateAttributes(
140146
fontSize: props.fontSize,
141147
fontFamily: props.fontFamily,
142148
fontWeight: props.fontWeight,
143-
inactiveColor: nil
149+
inactiveColor: props.inactiveTintColor
144150
)
145151

146152
items.forEach { item in
147153
item.setTitleTextAttributes(attributes, for: .normal)
154+
item.setTitleTextAttributes(selectedAttributes(props: props), for: .selected)
148155
}
149156
}
150157

151158
private func configureStandardAppearance(tabBar: UITabBar, props: TabViewProps) {
152159
let appearance = UITabBarAppearance()
160+
tabBar.tintColor = props.selectedActiveTintColor
161+
tabBar.unselectedItemTintColor = props.inactiveTintColor
153162

154163
// Configure background
155164
switch props.scrollEdgeAppearance {
@@ -180,8 +189,12 @@ struct TabViewImpl: View {
180189
if let inactiveTintColor = props.inactiveTintColor {
181190
itemAppearance.normal.iconColor = inactiveTintColor
182191
}
192+
if let activeTintColor = props.selectedActiveTintColor {
193+
itemAppearance.selected.iconColor = activeTintColor
194+
}
183195

184196
itemAppearance.normal.titleTextAttributes = attributes
197+
itemAppearance.selected.titleTextAttributes = selectedAttributes(props: props)
185198

186199
// Apply item appearance to all layouts
187200
appearance.stackedLayoutAppearance = itemAppearance
@@ -193,6 +206,49 @@ struct TabViewImpl: View {
193206
if #available(iOS 15.0, *) {
194207
tabBar.scrollEdgeAppearance = appearance.copy()
195208
}
209+
if let items = tabBar.items {
210+
configureTabBarItemImages(items: items, props: props)
211+
configureTabBarItemTitles(items: items, props: props)
212+
}
213+
}
214+
215+
private func configureTabBarItemTitles(items: [UITabBarItem], props: TabViewProps) {
216+
let normalAttributes = TabBarFontSize.createNormalStateAttributes(
217+
fontSize: props.fontSize,
218+
fontFamily: props.fontFamily,
219+
fontWeight: props.fontWeight,
220+
inactiveColor: props.inactiveTintColor
221+
)
222+
let selectedAttributes = selectedAttributes(props: props)
223+
224+
items.forEach { item in
225+
item.setTitleTextAttributes(normalAttributes, for: .normal)
226+
item.setTitleTextAttributes(selectedAttributes, for: .selected)
227+
}
228+
}
229+
230+
private func configureTabBarItemImages(items: [UITabBarItem], props: TabViewProps) {
231+
for (tabBarIndex, item) in items.enumerated() {
232+
guard let tabData = props.filteredItems[safe: tabBarIndex],
233+
let itemIndex = props.items.firstIndex(where: { $0.key == tabData.key }),
234+
let icon = props.icons[itemIndex] else { continue }
235+
236+
item.image = props.inactiveTintColor.map {
237+
icon.withTintColor($0, renderingMode: .alwaysOriginal)
238+
} ?? icon
239+
item.selectedImage = props.selectedActiveTintColor.map {
240+
icon.withTintColor($0, renderingMode: .alwaysOriginal)
241+
} ?? icon
242+
}
243+
}
244+
245+
private func selectedAttributes(props: TabViewProps) -> [NSAttributedString.Key: Any] {
246+
TabBarFontSize.createFontAttributes(
247+
size: props.fontSize.map(CGFloat.init) ?? TabBarFontSize.defaultSize,
248+
family: props.fontFamily,
249+
weight: props.fontWeight,
250+
color: props.selectedActiveTintColor
251+
)
196252
}
197253
#endif
198254

@@ -250,6 +306,9 @@ extension View {
250306
.onChange(of: props.selectedActiveTintColor) { _ in
251307
updateTabBarAppearance(props: props, tabBar: tabBar)
252308
}
309+
.onChange(of: props.iconsRevision) { _ in
310+
updateTabBarAppearance(props: props, tabBar: tabBar)
311+
}
253312
.onChange(of: props.fontSize) { _ in
254313
updateTabBarAppearance(props: props, tabBar: tabBar)
255314
}

packages/react-native-bottom-tabs/ios/TabViewProps.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class TabViewProps: ObservableObject {
5656
@Published var items: [TabInfo] = []
5757
@Published var selectedPage: String?
5858
@Published var icons: [Int: PlatformImage] = [:]
59+
@Published var iconsRevision: Int = 0
5960
@Published var sidebarAdaptable: Bool?
6061
@Published var labeled: Bool = false
6162
@Published var minimizeBehavior: MinimizeBehavior?

packages/react-native-bottom-tabs/ios/TabViewProvider.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,13 @@ public final class TabInfo: NSObject {
261261
guard let image else { return }
262262
DispatchQueue.main.async { [weak self] in
263263
guard let self else { return }
264-
props.icons[index] = image.resizeImageTo(size: iconSize)
264+
let icon = image.resizeImageTo(size: iconSize)
265+
#if os(macOS)
266+
props.icons[index] = icon
267+
#else
268+
props.icons[index] = icon?.withRenderingMode(.alwaysTemplate)
269+
#endif
270+
props.iconsRevision += 1
265271
}
266272
})
267273
}

0 commit comments

Comments
 (0)