From 15c8584a2331769ca0fb4353cd741dfb004f8886 Mon Sep 17 00:00:00 2001 From: contra Date: Fri, 30 Jan 2026 21:45:41 -0700 Subject: [PATCH] feat(ios): add ignoresContentBackground prop for iOS 26 liquid glass control iOS 26 introduced a new "liquid glass" material effect for tab bars that dynamically samples colors from the content behind it. While this creates beautiful translucent effects for static content, it can cause readability issues when the content is dynamic (like maps) or has varying colors. This PR adds a new `ignoresContentBackground` prop that, when set to true, disables the liquid glass content sampling on iOS 26+ and instead uses a solid background color (from `tabBarStyle.backgroundColor` / `barTintColor`). Usage: ```tsx ``` Or with @bottom-tabs/react-navigation: ```tsx {/* screens */} ``` Implementation details: - Added `ignoresContentBackground` prop to TabViewNativeComponent.ts (codegen) - Added `ignoresContentBackground` to TabViewProps.swift - Added prop bridging in RCTTabViewComponentView.mm - Added SwiftUI `.toolbarBackgroundVisibility(.visible)` and `.toolbarBackground()` modifiers for iOS 26+ in TabViewImpl.swift - Added documentation in TabView.tsx This prop only has an effect on iOS 26+. On earlier iOS versions and other platforms, it is ignored. --- .../ios/RCTTabViewComponentView.mm | 3 +++ .../ios/TabViewImpl.swift | 23 +++++++++++++++++++ .../ios/TabViewProps.swift | 1 + .../react-native-bottom-tabs/src/TabView.tsx | 12 ++++++++++ .../src/TabViewNativeComponent.ts | 1 + 5 files changed, 40 insertions(+) diff --git a/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm b/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm index 92cab0e7..084c2d8f 100644 --- a/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm +++ b/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm @@ -180,6 +180,9 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & _tabViewProvider.tabBarHidden = newViewProps.tabBarHidden; } + if (oldViewProps.ignoresContentBackground != newViewProps.ignoresContentBackground) { + _tabViewProvider.ignoresContentBackground = newViewProps.ignoresContentBackground; + } [super updateProps:props oldProps:oldProps]; } diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift index 72938be9..4c4c88ff 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift @@ -45,6 +45,7 @@ struct TabViewImpl: View { var body: some View { tabContent .tabBarMinimizeBehavior(props.minimizeBehavior) + .ignoresContentBackground(props.ignoresContentBackground, barTintColor: props.barTintColor) #if !os(tvOS) && !os(macOS) && !os(visionOS) .onTabItemEvent { index, isLongPress in let item = props.filteredItems[safe: index] @@ -325,4 +326,26 @@ extension View { self } } + + /// Disables iOS 26 liquid glass content sampling and uses a solid background instead. + /// When enabled, the tab bar will use the specified barTintColor (or system default if nil) + /// instead of dynamically sampling colors from the content behind it. + @ViewBuilder + func ignoresContentBackground(_ ignores: Bool, barTintColor: PlatformColor?) -> some View { + #if compiler(>=6.2) + if #available(iOS 26.0, *) { + if ignores { + self + .toolbarBackgroundVisibility(.visible, for: .tabBar) + .toolbarBackground(barTintColor.map { Color($0) } ?? Color(.systemBackground), for: .tabBar) + } else { + self + } + } else { + self + } + #else + self + #endif + } } diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift index 9cfb29a9..a9edfc7e 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift @@ -71,6 +71,7 @@ class TabViewProps: ObservableObject { @Published var fontFamily: String? @Published var fontWeight: String? @Published var tabBarHidden: Bool = false + @Published var ignoresContentBackground: Bool = false var selectedActiveTintColor: PlatformColor? { if let selectedPage, diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx index 4e93f090..e98ed9fe 100644 --- a/packages/react-native-bottom-tabs/src/TabView.tsx +++ b/packages/react-native-bottom-tabs/src/TabView.tsx @@ -212,6 +212,18 @@ interface Props { * @default 'locale' */ layoutDirection?: LayoutDirection; + /** + * When true, disables iOS 26 liquid glass content sampling and uses a solid + * background color (from tabBarStyle.backgroundColor) instead of dynamically + * sampling colors from the content behind the tab bar. (iOS 26+ only) + * + * Use this when you have dynamic content (like maps) where the liquid glass + * effect causes the tab bar to have inconsistent or unreadable colors. + * + * @platform ios + * @default false + */ + ignoresContentBackground?: boolean; } const ANDROID_MAX_TABS = 100; diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts index 50082c55..3aa3b180 100644 --- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts +++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts @@ -61,6 +61,7 @@ export interface TabViewProps extends ViewProps { fontFamily?: string; fontWeight?: string; fontSize?: Int32; + ignoresContentBackground?: boolean; } export default codegenNativeComponent('RNCTabView', {