Skip to content

Commit 11b832c

Browse files
committed
feat(ios): adding tint colors for items
1 parent 3aa9bbf commit 11b832c

8 files changed

+105
-17
lines changed

example/src/Examples/TintColors.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default function TintColorsExample() {
2020
title: 'Albums',
2121
focusedIcon: require('../../assets/icons/grid_dark.png'),
2222
badge: '5',
23+
activeTintColor: 'green',
2324
},
2425
{
2526
key: 'contacts',
@@ -46,8 +47,8 @@ export default function TintColorsExample() {
4647
navigationState={{ index, routes }}
4748
onIndexChange={setIndex}
4849
renderScene={renderScene}
49-
activeTintColor="red"
50-
inactiveTintColor="blue"
50+
inactiveTintColor="red"
51+
scrollEdgeAppearance="default"
5152
/>
5253
);
5354
}

ios/Extensions.swift

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ extension Collection {
88
}
99
}
1010

11+
12+
extension Collection where Element == TabInfo {
13+
func findByKey(_ key: String?) -> Element? {
14+
guard let key else { return nil }
15+
guard !isEmpty else { return nil }
16+
return first(where: { $0.key == key })
17+
}
18+
}
19+
1120
extension UIView {
1221
func pinEdges(to other: UIView) {
1322
NSLayoutConstraint.activate([

ios/RCTTabViewViewManager.mm

-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,5 @@ - (UIView *)view
3434
RCT_EXPORT_VIEW_PROPERTY(scrollEdgeAppearance, NSString)
3535
RCT_EXPORT_VIEW_PROPERTY(activeTintColor, NSNumber)
3636
RCT_EXPORT_VIEW_PROPERTY(inactiveTintColor, NSNumber)
37-
RCT_EXPORT_VIEW_PROPERTY(activeTintColor, NSNumber)
38-
RCT_EXPORT_VIEW_PROPERTY(inactiveTintColor, NSNumber)
3937

4038
@end

ios/TabViewImpl.swift

+79-11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ struct TabViewImpl: View {
3939
@ObservedObject var props: TabViewProps
4040
var onSelect: (_ key: String) -> Void
4141

42+
var selectedActiveTintColor: UIColor? {
43+
// check first in selected tab
44+
let selectedPage = props.selectedPage
45+
if let selectedPage {
46+
if let tabData = props.items?.tabs.findByKey(selectedPage) {
47+
if let activeTintColor = tabData.activeTintColor {
48+
return RCTConvert.uiColor(activeTintColor)
49+
}
50+
}
51+
}
52+
53+
if let activeTintColor = props.activeTintColor {
54+
return activeTintColor
55+
}
56+
57+
return nil
58+
}
59+
4260
var body: some View {
4361
TabView(selection: $props.selectedPage) {
4462
ForEach(props.children?.indices ?? 0..<0, id: \.self) { index in
@@ -63,7 +81,7 @@ struct TabViewImpl: View {
6381
.tabBadge(tabData?.badge)
6482
}
6583
}
66-
.tintColor(props.activeTintColor)
84+
.tintColor(selectedActiveTintColor)
6785
.getSidebarAdaptable(enabled: props.sidebarAdaptable ?? false)
6886
.onChange(of: props.selectedPage ?? "") { newValue in
6987
if (props.disablePageAnimations) {
@@ -72,13 +90,37 @@ struct TabViewImpl: View {
7290
UIView.setAnimationsEnabled(true)
7391
}
7492
}
93+
94+
// to apply active tint color per tab
95+
let scrollEdgeAppearance = configureAppearance(for: props.scrollEdgeAppearance ?? "")
96+
let colorTintAppearance = configureAppearance(appearance: scrollEdgeAppearance, inactiveTint: props.inactiveTintColor, activeTint: selectedActiveTintColor)
97+
setupTabBarAppearance(colorTintAppearance)
98+
7599
onSelect(newValue)
76100
}
77101
.onChange(of: props.scrollEdgeAppearance) { newValue in
78-
if #available(iOS 15.0, *) {
79-
UITabBar.appearance().scrollEdgeAppearance = configureAppearance(for: newValue ?? "")
80-
}
81-
UITabBar.appearance().standardAppearance = configureAppearance(for: newValue ?? "")
102+
let scrollEdgeAppearance = configureAppearance(for: newValue ?? "")
103+
let colorTintAppearance = configureAppearance(appearance: scrollEdgeAppearance, inactiveTint: props.inactiveTintColor, activeTint: selectedActiveTintColor)
104+
105+
setupTabBarAppearance(colorTintAppearance)
106+
}
107+
.onChange(of: props.inactiveTintColor) { newValue in
108+
let scrollEdgeAppearance = configureAppearance(for: props.scrollEdgeAppearance ?? "")
109+
let colorTintAppearance = configureAppearance(appearance: scrollEdgeAppearance, inactiveTint: newValue, activeTint: selectedActiveTintColor)
110+
111+
setupTabBarAppearance(colorTintAppearance)
112+
}
113+
.onChange(of: selectedActiveTintColor) { newValue in
114+
let scrollEdgeAppearance = configureAppearance(for: props.scrollEdgeAppearance ?? "")
115+
let colorTintAppearance = configureAppearance(appearance: scrollEdgeAppearance, inactiveTint: props.inactiveTintColor, activeTint: newValue)
116+
117+
setupTabBarAppearance(colorTintAppearance)
118+
}
119+
.onAppear {
120+
// we have to keep onAppear to setup the appearance for the first render.
121+
let scrollEdgeAppearance = configureAppearance(for: props.scrollEdgeAppearance ?? "")
122+
let colorTintAppearance = configureAppearance(appearance: scrollEdgeAppearance, inactiveTint: props.inactiveTintColor, activeTint: selectedActiveTintColor)
123+
setupTabBarAppearance(colorTintAppearance)
82124
}
83125
}
84126
}
@@ -95,19 +137,45 @@ private func configureAppearance(for appearanceType: String) -> UITabBarAppearan
95137
appearance.configureWithDefaultBackground()
96138
}
97139

140+
return appearance
141+
}
142+
143+
private func configureAppearance(appearance: UITabBarAppearance, inactiveTint inactiveTintColor: UIColor?, activeTint activeTintColor: UIColor?) -> UITabBarAppearance {
98144
// @see https://stackoverflow.com/a/71934882
99-
if let inactiveTintColor = props.inactiveTintColor {
100-
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor(inactiveTintColor)]
101-
appearance.stackedLayoutAppearance.normal.iconColor = UIColor(inactiveTintColor)
145+
if let inactiveTintColor {
146+
setTabBarItemColors(appearance.stackedLayoutAppearance, inactiveColor: inactiveTintColor)
147+
setTabBarItemColors(appearance.inlineLayoutAppearance, inactiveColor: inactiveTintColor)
148+
setTabBarItemColors(appearance.compactInlineLayoutAppearance, inactiveColor: inactiveTintColor)
102149
}
103-
if let activeTintColor = props.activeTintColor {
104-
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: UIColor(activeTintColor)]
105-
appearance.stackedLayoutAppearance.selected.iconColor = UIColor(activeTintColor)
150+
151+
if let activeTintColor {
152+
setTabBarItemColors(appearance.stackedLayoutAppearance, activeColor: activeTintColor)
153+
setTabBarItemColors(appearance.inlineLayoutAppearance, activeColor: activeTintColor)
154+
setTabBarItemColors(appearance.compactInlineLayoutAppearance, activeColor: activeTintColor)
106155
}
107156

108157
return appearance
109158
}
110159

160+
private func setupTabBarAppearance(_ appearance: UITabBarAppearance) {
161+
if #available(iOS 15.0, *) {
162+
UITabBar.appearance().scrollEdgeAppearance = appearance
163+
}
164+
UITabBar.appearance().standardAppearance = appearance
165+
}
166+
167+
private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance, inactiveColor: UIColor) {
168+
itemAppearance.normal.iconColor = inactiveColor
169+
itemAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveColor]
170+
}
171+
172+
173+
private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance, activeColor: UIColor) {
174+
itemAppearance.selected.iconColor = activeColor
175+
itemAppearance.selected.titleTextAttributes = [.foregroundColor: activeColor]
176+
}
177+
178+
111179
struct TabItem: View {
112180
var title: String?
113181
var icon: UIImage?

ios/TabViewProvider.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ struct TabInfo: Codable {
77
let title: String
88
let badge: String
99
let sfSymbol: String
10+
let activeTintColor: Int?
1011
}
1112

1213
struct TabData: Codable {
@@ -157,7 +158,8 @@ struct TabData: Codable {
157158
key: itemDict["key"] as? String ?? "",
158159
title: itemDict["title"] as? String ?? "",
159160
badge: itemDict["badge"] as? String ?? "",
160-
sfSymbol: itemDict["sfSymbol"] as? String ?? ""
161+
sfSymbol: itemDict["sfSymbol"] as? String ?? "",
162+
activeTintColor: itemDict["activeTintColor"] as? Int
161163
)
162164
)
163165
}

src/TabView.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type { TabViewItems } from './TabViewNativeComponent';
2-
import { ColorValue, Image, Platform, StyleSheet, View } from 'react-native';
2+
import {
3+
ColorValue,
4+
Image,
5+
Platform,
6+
processColor,
7+
StyleSheet,
8+
View,
9+
} from 'react-native';
310

411
//@ts-ignore
512
import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
@@ -156,6 +163,7 @@ const TabView = <Route extends BaseRoute>({
156163
title: getLabelText({ route }) ?? route.key,
157164
sfSymbol: isSfSymbol ? icon.sfSymbol : undefined,
158165
badge: props.getBadge?.({ route }),
166+
activeTintColor: processColor(route.activeTintColor),
159167
};
160168
}),
161169
[getLabelText, icons, trimmedRoutes, props]

src/TabViewNativeComponent.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type TabViewItems = ReadonlyArray<{
1313
title: string;
1414
sfSymbol?: string;
1515
badge?: string;
16+
activeTintColor?: ProcessedColorValue | null;
1617
}>;
1718

1819
export interface TabViewProps extends ViewProps {

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type BaseRoute = {
1212
lazy?: boolean;
1313
focusedIcon?: ImageSourcePropType | AppleIcon;
1414
unfocusedIcon?: ImageSourcePropType | AppleIcon;
15+
activeTintColor?: string;
1516
};
1617

1718
export type NavigationState<Route extends BaseRoute> = {

0 commit comments

Comments
 (0)