From 3b1706170dec44d30edef850537b08cfc6acfdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 17 Jun 2025 20:11:03 +0200 Subject: [PATCH 1/2] feat: implement iOS 26 minimize behavior --- apps/example/ios/Podfile.lock | 4 ++-- apps/example/src/Examples/SFSymbols.tsx | 1 + .../ios/Fabric/RCTTabViewComponentView.mm | 4 ++++ .../ios/RCTTabViewViewManager.mm | 1 + .../ios/TabViewImpl.swift | 14 ++++++++++++ .../ios/TabViewProps.swift | 22 +++++++++++++++++++ .../ios/TabViewProvider.swift | 6 +++++ .../react-native-bottom-tabs/src/TabView.tsx | 5 +++++ .../src/TabViewNativeComponent.ts | 1 + 9 files changed, 56 insertions(+), 2 deletions(-) diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock index 92798c75..b0a038e8 100644 --- a/apps/example/ios/Podfile.lock +++ b/apps/example/ios/Podfile.lock @@ -1180,7 +1180,7 @@ PODS: - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - react-native-bottom-tabs (0.9.1): + - react-native-bottom-tabs (0.9.2): - DoubleConversion - glog - RCT-Folly (= 2024.11.18.00) @@ -1931,7 +1931,7 @@ SPEC CHECKSUMS: React-logger: 1935d6e6461e9c8be4c87af56c56a4876021171e React-Mapbuffer: 212171f037e3b22e6c2df839aa826806da480b85 React-microtasksnativemodule: a0ffd165c39251512d8cf51e9e8f5719dabc38b6 - react-native-bottom-tabs: 7c7ee94435e518759b414e89068956096a1adec7 + react-native-bottom-tabs: 6ee03990297f7e37f5c1dd6f5259cee851733d4f react-native-safe-area-context: e54b360402f089600c2fb0d825d1d3d918b99e15 React-nativeconfig: cb207ebba7cafce30657c7ad9f1587a8f32e4564 React-NativeModulesApple: 76a5d35322908fbc88871e6dd20433bea2b8b2db diff --git a/apps/example/src/Examples/SFSymbols.tsx b/apps/example/src/Examples/SFSymbols.tsx index 591424de..1e7b48ca 100644 --- a/apps/example/src/Examples/SFSymbols.tsx +++ b/apps/example/src/Examples/SFSymbols.tsx @@ -47,6 +47,7 @@ export default function SFSymbols() { return ( ); RCT_EXPORT_VIEW_PROPERTY(sidebarAdaptable, BOOL) RCT_EXPORT_VIEW_PROPERTY(labeled, BOOL) diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift index f87934f3..19d7f514 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift @@ -28,6 +28,7 @@ struct TabViewImpl: View { onLayout(size) } } + .tabBarMinimizeBehavior(props.minimizeBehavior) #if !os(tvOS) && !os(macOS) && !os(visionOS) .onTabItemEvent { index, isLongPress in let item = props.filteredItems[safe: index] @@ -333,6 +334,19 @@ extension View { } } + @ViewBuilder + func tabBarMinimizeBehavior(_ behavior: MinimizeBehavior?) -> some View { + if #available(iOS 26.0, *) { + if let behavior { + self.tabBarMinimizeBehavior(behavior.convert()) + } else { + self + } + } else { + self + } + } + @ViewBuilder func hideTabBar(_ flag: Bool) -> some View { #if !os(macOS) diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift index 131c7cfb..c925d82e 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift @@ -1,5 +1,26 @@ import SwiftUI +internal enum MinimizeBehavior: String { + case automatic + case never + case onScrollUp + case onScrollDown + + @available(iOS 26.0, *) + func convert() -> TabBarMinimizeBehavior { + switch self { + case .automatic: + return .automatic + case .never: + return .never + case .onScrollUp: + return .onScrollUp + case .onScrollDown: + return .onScrollDown + } + } +} + /** Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes. */ @@ -10,6 +31,7 @@ class TabViewProps: ObservableObject { @Published var icons: [Int: PlatformImage] = [:] @Published var sidebarAdaptable: Bool? @Published var labeled: Bool = false + @Published var minimizeBehavior: MinimizeBehavior? @Published var scrollEdgeAppearance: String? @Published var barTintColor: PlatformColor? @Published var activeTintColor: PlatformColor? diff --git a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift index 135c9a95..4c2e0003 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift @@ -102,6 +102,12 @@ public final class TabInfo: NSObject { } } + @objc public var minimizeBehavior: NSString? { + didSet { + props.minimizeBehavior = MinimizeBehavior(rawValue: minimizeBehavior as? String ?? "") + } + } + @objc public var translucent: Bool = true { didSet { props.translucent = translucent diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx index dd71356e..2921f88b 100644 --- a/packages/react-native-bottom-tabs/src/TabView.tsx +++ b/packages/react-native-bottom-tabs/src/TabView.tsx @@ -54,6 +54,11 @@ interface Props { * Describes the appearance attributes for the tabBar to use when an observable scroll view is scrolled to the bottom. (iOS only) */ scrollEdgeAppearance?: 'default' | 'opaque' | 'transparent'; + + /** + * Behavior for minimizing the tab bar. (iOS 26+) + */ + minimizeBehavior?: 'automatic' | 'onScrollDown' | 'onScrollUp' | 'never'; /** * Active tab color. */ diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts index 1a7224d8..4eca038b 100644 --- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts +++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts @@ -52,6 +52,7 @@ export interface TabViewProps extends ViewProps { disablePageAnimations?: boolean; activeIndicatorColor?: ColorValue; hapticFeedbackEnabled?: boolean; + minimizeBehavior?: string; fontFamily?: string; fontWeight?: string; fontSize?: Int32; From b0a212066dbbdc814fb7c0d8a91adaa0527cc4fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Sun, 6 Jul 2025 16:15:12 +0200 Subject: [PATCH 2/2] feat: add docs --- .changeset/ready-paths-train.md | 5 +++++ docs/docs/docs/guides/standalone-usage.md | 17 +++++++++++++++++ .../docs/guides/usage-with-react-navigation.mdx | 17 +++++++++++++++++ .../android/src/newarch/RCTTabViewManager.kt | 3 +++ .../android/src/oldarch/RCTTabViewManager.kt | 4 ++++ .../ios/TabViewImpl.swift | 4 ++++ .../ios/TabViewProps.swift | 2 ++ 7 files changed, 52 insertions(+) create mode 100644 .changeset/ready-paths-train.md diff --git a/.changeset/ready-paths-train.md b/.changeset/ready-paths-train.md new file mode 100644 index 00000000..3e7de55a --- /dev/null +++ b/.changeset/ready-paths-train.md @@ -0,0 +1,5 @@ +--- +'react-native-bottom-tabs': patch +--- + +feat: implement iOS 26 minimizeBehavior feature diff --git a/docs/docs/docs/guides/standalone-usage.md b/docs/docs/docs/guides/standalone-usage.md index 5b671654..a2766d21 100644 --- a/docs/docs/docs/guides/standalone-usage.md +++ b/docs/docs/docs/guides/standalone-usage.md @@ -47,6 +47,7 @@ export default function TabViewExample() { renderScene={renderScene} onIndexChange={setIndex} labeled + minimizeBehavior="onScrollDown" // iOS 26+: Hide tab bar when scrolling down /> ); } @@ -143,6 +144,22 @@ Supported properties: Appearance attributes for the tab bar when a scroll view is at the bottom. - Type: `'default' | 'opaque' | 'transparent'` +#### `minimizeBehavior` + +Controls how the tab bar behaves when content is scrolled. +- Type: `'automatic' | 'onScrollDown' | 'onScrollUp' | 'never'` +- Default: `undefined` (uses system default) + +Options: +- `automatic`: Platform determines the behavior +- `onScrollDown`: Tab bar minimizes when scrolling down +- `onScrollUp`: Tab bar minimizes when scrolling up +- `never`: Tab bar never minimizes + +:::note +This feature requires iOS 26.0 or later and is only available on iOS. On older versions, this prop is ignored. +::: + #### `tabBarActiveTintColor` Color for the active tab. diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index 97b08547..9efcea87 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -158,6 +158,23 @@ Tab views using the sidebar adaptable style have an appearance Whether to enable haptic feedback on tab press. Defaults to false. +#### `minimizeBehavior` + +Controls how the tab bar behaves when content is scrolled. + +- Type: `'automatic' | 'onScrollDown' | 'onScrollUp' | 'never'` +- Default: `undefined` (uses system default) + +Options: +- `automatic`: Platform determines the behavior +- `onScrollDown`: Tab bar minimizes when scrolling down +- `onScrollUp`: Tab bar minimizes when scrolling up +- `never`: Tab bar never minimizes + +:::note +This feature requires iOS 26.0 or later and is only available on iOS. On older versions, this prop is ignored. +::: + #### `tabLabelStyle` Object containing styles for the tab label. diff --git a/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt b/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt index 0f221a16..59be7cab 100644 --- a/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt +++ b/packages/react-native-bottom-tabs/android/src/newarch/RCTTabViewManager.kt @@ -163,4 +163,7 @@ class RCTTabViewManager(context: ReactApplicationContext) : override fun setScrollEdgeAppearance(view: ReactBottomNavigationView?, value: String?) { } + + override fun setMinimizeBehavior(view: ReactBottomNavigationView?, value: String?) { + } } diff --git a/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt b/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt index 9a375ec2..50eb1f50 100644 --- a/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt +++ b/packages/react-native-bottom-tabs/android/src/oldarch/RCTTabViewManager.kt @@ -137,6 +137,10 @@ class RCTTabViewManager(context: ReactApplicationContext) : ViewGroupManager some View { +#if compiler(>=6.2) if #available(iOS 26.0, *) { if let behavior { self.tabBarMinimizeBehavior(behavior.convert()) @@ -345,6 +346,9 @@ extension View { } else { self } +#else + self +#endif } @ViewBuilder diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift index c925d82e..833aca94 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift @@ -6,6 +6,7 @@ internal enum MinimizeBehavior: String { case onScrollUp case onScrollDown +#if compiler(>=6.2) @available(iOS 26.0, *) func convert() -> TabBarMinimizeBehavior { switch self { @@ -19,6 +20,7 @@ internal enum MinimizeBehavior: String { return .onScrollDown } } +#endif } /**