From 6a2f41b0d933813df8f381c39e0a30679dfa26e6 Mon Sep 17 00:00:00 2001 From: shubham <85783070+shubhamguptadream11@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:00:45 +0530 Subject: [PATCH] Added support for barTintColor in android ios (#55) * feat: added support for bar tint color on android ios * fix: added color in example * fix: pr comments resovled android * fix: pr comments resovled android * feat: refactored code --- .../main/java/com/rcttabview/RCTTabView.kt | 22 ++++++++ .../com/rcttabview/RCTTabViewViewManager.kt | 5 ++ .../guides/usage-with-react-navigation.mdx | 4 ++ example/src/App.tsx | 8 +++ example/src/Examples/FourTabs.tsx | 3 ++ ios/RCTTabViewViewManager.mm | 1 + ios/TabViewImpl.swift | 50 ++++++++++++------- ios/TabViewProvider.swift | 6 +++ src/TabView.tsx | 6 +++ src/TabViewNativeComponent.ts | 1 + 10 files changed, 89 insertions(+), 17 deletions(-) diff --git a/android/src/main/java/com/rcttabview/RCTTabView.kt b/android/src/main/java/com/rcttabview/RCTTabView.kt index e4f31bf..c47bc29 100644 --- a/android/src/main/java/com/rcttabview/RCTTabView.kt +++ b/android/src/main/java/com/rcttabview/RCTTabView.kt @@ -4,10 +4,13 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable +import android.util.TypedValue import android.net.Uri import android.view.Choreographer import android.view.MenuItem +import androidx.appcompat.content.res.AppCompatResources import com.facebook.common.references.CloseableReference import com.facebook.datasource.DataSources import com.facebook.drawee.backends.pipeline.Fresco @@ -146,4 +149,23 @@ class ReactBottomNavigationView(context: Context) : BottomNavigationView(context super.onDetachedFromWindow() isAnimating = false } + + fun setBarTintColor(color: Int?) { + // Set the color, either using the active background color or a default color. + val backgroundColor = color ?: getDefaultColorFor(android.R.attr.colorPrimary) ?: return + + // Apply the same color to both active and inactive states + val colorDrawable = ColorDrawable(backgroundColor) + + itemBackground = colorDrawable + } + + private fun getDefaultColorFor(baseColorThemeAttr: Int): Int? { + val value = TypedValue() + if (!context.theme.resolveAttribute(baseColorThemeAttr, value, true)) { + return null + } + val baseColor = AppCompatResources.getColorStateList(context, value.resourceId) + return baseColor.defaultColor + } } diff --git a/android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt b/android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt index d1495b8..ed374c8 100644 --- a/android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt +++ b/android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt @@ -68,6 +68,11 @@ class RCTTabViewViewManager : view.setIcons(icons) } + @ReactProp(name = "barTintColor") + fun setBarTintColor(view: ReactBottomNavigationView, color: Int?) { + view.setBarTintColor(color) + } + @ReactProp(name = "rippleColor") fun setRippleColor(view: ReactBottomNavigationView, rippleColor: Int?) { if (rippleColor != null) { diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index 811e7c1..ebe5f25 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -65,6 +65,10 @@ Whether to disable page animations between tabs. Describes the appearance attributes for the tabBar to use when an observable scroll view is scrolled to the bottom. +#### `barTintColor` + +Background color of the tab bar. + #### `translucent` A Boolean value that indicates whether the tab bar is translucent. diff --git a/example/src/App.tsx b/example/src/App.tsx index bc97be0..647c574 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -39,6 +39,10 @@ const FourTabsTransparentScrollEdgeAppearance = () => { return ; }; +const FourTabsWithBarTintColor = () => { + return ; +}; + const FourTabsTranslucent = () => { return ; }; @@ -62,6 +66,10 @@ const examples = [ component: FourTabsTransparentScrollEdgeAppearance, name: 'Four Tabs - Transparent scroll edge appearance', }, + { + component: FourTabsWithBarTintColor, + name: 'Four Tabs - Custom Background Color of Tabs', + }, { component: FourTabsTranslucent, name: 'Four Tabs - Translucent tab bar', diff --git a/example/src/Examples/FourTabs.tsx b/example/src/Examples/FourTabs.tsx index c6ceceb..5c474c7 100644 --- a/example/src/Examples/FourTabs.tsx +++ b/example/src/Examples/FourTabs.tsx @@ -10,6 +10,7 @@ interface Props { ignoresTopSafeArea?: boolean; disablePageAnimations?: boolean; scrollEdgeAppearance?: 'default' | 'opaque' | 'transparent'; + barTintColor?: ColorValue; translucent?: boolean; rippleColor?: ColorValue; } @@ -18,6 +19,7 @@ export default function FourTabs({ ignoresTopSafeArea = false, disablePageAnimations = false, scrollEdgeAppearance = 'default', + barTintColor, translucent = true, rippleColor, }: Props) { @@ -64,6 +66,7 @@ export default function FourTabs({ navigationState={{ index, routes }} onIndexChange={setIndex} renderScene={renderScene} + barTintColor={barTintColor} translucent={translucent} rippleColor={rippleColor} /> diff --git a/ios/RCTTabViewViewManager.mm b/ios/RCTTabViewViewManager.mm index c4acb2a..6ca85e8 100644 --- a/ios/RCTTabViewViewManager.mm +++ b/ios/RCTTabViewViewManager.mm @@ -32,6 +32,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(ignoresTopSafeArea, BOOL) RCT_EXPORT_VIEW_PROPERTY(disablePageAnimations, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollEdgeAppearance, NSString) +RCT_EXPORT_VIEW_PROPERTY(barTintColor, NSNumber) RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL) @end diff --git a/ios/TabViewImpl.swift b/ios/TabViewImpl.swift index 4c844b0..571fb40 100644 --- a/ios/TabViewImpl.swift +++ b/ios/TabViewImpl.swift @@ -15,6 +15,7 @@ class TabViewProps: ObservableObject { @Published var ignoresTopSafeArea: Bool? @Published var disablePageAnimations: Bool = false @Published var scrollEdgeAppearance: String? + @Published var barTintColor: UIColor? @Published var translucent: Bool = true } @@ -63,7 +64,6 @@ struct TabViewImpl: View { } } .getSidebarAdaptable(enabled: props.sidebarAdaptable ?? false) - .tabBarTranslucent(props.translucent) .onChange(of: props.selectedPage ?? "") { newValue in if (props.disablePageAnimations) { UIView.setAnimationsEnabled(false) @@ -73,16 +73,22 @@ struct TabViewImpl: View { } onSelect(newValue) } + .onAppear() { + updateTabBarAppearance(props: props) + } + .onChange(of: props.barTintColor) { newValue in + updateTabBarAppearance(props: props) + } .onChange(of: props.scrollEdgeAppearance) { newValue in - if #available(iOS 15.0, *) { - UITabBar.appearance().scrollEdgeAppearance = configureAppearance(for: newValue ?? "") - } + updateTabBarAppearance(props: props) + } + .onChange(of: props.translucent) { newValue in + updateTabBarAppearance(props: props) } } } -private func configureAppearance(for appearanceType: String) -> UITabBarAppearance { - let appearance = UITabBarAppearance() +private func configureAppearance(for appearanceType: String, appearance: UITabBarAppearance) -> UITabBarAppearance { switch appearanceType { case "opaque": @@ -96,6 +102,27 @@ private func configureAppearance(for appearanceType: String) -> UITabBarAppearan return appearance } +private func updateTabBarAppearance(props: TabViewProps) { + if #available(iOS 15.0, *) { + let appearance = UITabBarAppearance() + + UITabBar.appearance().scrollEdgeAppearance = configureAppearance(for: props.scrollEdgeAppearance ?? "", appearance: appearance) + + if props.translucent == false { + appearance.configureWithOpaqueBackground() + } + + if props.barTintColor != nil { + appearance.backgroundColor = props.barTintColor + } + + UITabBar.appearance().standardAppearance = appearance + } else { + UITabBar.appearance().barTintColor = props.barTintColor + UITabBar.appearance().isTranslucent = props.translucent + } +} + struct TabItem: View { var title: String? var icon: UIImage? @@ -159,15 +186,4 @@ extension View { .frame(idealWidth: frame.width, idealHeight: frame.height) } } - - @ViewBuilder - func tabBarTranslucent(_ translucent: Bool) -> some View { - self - .onAppear { - UITabBar.appearance().isTranslucent = translucent - } - .onChange(of: translucent) { newValue in - UITabBar.appearance().isTranslucent = newValue - } - } } diff --git a/ios/TabViewProvider.swift b/ios/TabViewProvider.swift index 5caf4f7..6349042 100644 --- a/ios/TabViewProvider.swift +++ b/ios/TabViewProvider.swift @@ -76,6 +76,12 @@ struct TabData: Codable { props.items = parseTabData(from: items) } } + + @objc var barTintColor: NSNumber? { + didSet { + props.barTintColor = RCTConvert.uiColor(barTintColor) + } + } @objc public convenience init(eventDispatcher: RCTEventDispatcherProtocol, imageLoader: RCTImageLoader) { self.init() diff --git a/src/TabView.tsx b/src/TabView.tsx index 3cc0182..005ae02 100644 --- a/src/TabView.tsx +++ b/src/TabView.tsx @@ -85,6 +85,10 @@ interface Props { focused: boolean; }) => ImageSource | undefined; + /** + * Background color of the tab bar. + */ + barTintColor?: ColorValue; /** * A Boolean value that indicates whether the tab bar is translucent. (iOS only) */ @@ -106,6 +110,7 @@ const TabView = ({ ? route.focusedIcon : route.unfocusedIcon : route.focusedIcon, + barTintColor, ...props }: Props) => { // @ts-ignore @@ -192,6 +197,7 @@ const TabView = ({ onPageSelected={({ nativeEvent: { key } }) => { jumpTo(key); }} + barTintColor={processColor(barTintColor)} {...props} rippleColor={processColor(props.rippleColor)} > diff --git a/src/TabViewNativeComponent.ts b/src/TabViewNativeComponent.ts index a442c8a..f7a44ee 100644 --- a/src/TabViewNativeComponent.ts +++ b/src/TabViewNativeComponent.ts @@ -23,6 +23,7 @@ export interface TabViewProps extends ViewProps { labeled?: boolean; sidebarAdaptable?: boolean; scrollEdgeAppearance?: string; + barTintColor?: ProcessedColorValue | null; translucent?: boolean; rippleColor?: ProcessedColorValue | null; }