Skip to content

Commit ca8fed7

Browse files
committed
Merge branch 'main' into fix/tab-bar-hidden-respects-user-prop
2 parents 8d45171 + 6797d32 commit ca8fed7

20 files changed

Lines changed: 568 additions & 68 deletions

.changeset/bright-taxes-cheat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-bottom-tabs': patch
3+
---
4+
5+
Fix phantom tab switch on iOS when scene re-appears

.changeset/cute-carrots-switch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-bottom-tabs': patch
3+
---
4+
5+
Fix screen not rendering after tab change on iOS 27

.changeset/grumpy-otters-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-bottom-tabs': patch
3+
---
4+
5+
Disable tabBarInactiveTintColor on iOS >= 26

.changeset/tidy-eels-wish.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-bottom-tabs': minor
3+
---
4+
5+
Add `experimental_bakedTintColors` prop to opt into the iOS 26 Liquid Glass active and inactive tint color workaround.

.gitignore

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
#
33
.DS_Store
44

5-
# XDE
6-
.expo/
7-
85
# VSCode
96
.vscode/
107
jsconfig.json
@@ -82,3 +79,9 @@ lib/
8279
# React Native Codegen
8380
ios/generated
8481
android/generated
82+
83+
# Codex
84+
.codex
85+
86+
# Agent Device
87+
tmp/

apps/example/src/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ import NativeBottomTabsUnmounting from './Examples/NativeBottomTabsUnmounting';
3636
import NativeBottomTabsCustomTabBar from './Examples/NativeBottomTabsCustomTabBar';
3737
import NativeBottomTabsFreezeOnBlur from './Examples/NativeBottomTabsFreezeOnBlur';
3838
import NativeBottomTabsScreenLayout from './Examples/NativeBottomTabsScreenLayout';
39+
import NativeBottomTabsLazy from './Examples/NativeBottomTabsLazy';
3940
import BottomAccessoryView from './Examples/BottomAccessoryView';
4041
import { useLogger } from '@react-navigation/devtools';
42+
import LazyTabs from './Examples/LazyTabs';
43+
import { LogBox } from 'react-native';
44+
45+
LogBox.ignoreAllLogs();
4146

4247
const HiddenTab = () => {
4348
return <FourTabs hideOneTab />;
@@ -74,6 +79,7 @@ const FourTabsActiveIndicatorColor = () => {
7479
const UnlabeledTabs = () => {
7580
return <LabeledTabs showLabels={false} />;
7681
};
82+
7783
const FourTabsRightToLeft = () => {
7884
return <FourTabsRTL layoutDirection={'rtl'} />;
7985
};
@@ -96,6 +102,7 @@ const examples = [
96102
name: 'Embedded stacks',
97103
screenOptions: { headerShown: false },
98104
},
105+
{ component: LazyTabs, name: 'Lazy Tabs' },
99106
{
100107
component: FourTabsRippleColor,
101108
name: 'Four Tabs with ripple Color',
@@ -156,6 +163,10 @@ const examples = [
156163
component: NativeBottomTabsScreenLayout,
157164
name: 'Native Bottom Tabs with screenLayout',
158165
},
166+
{
167+
component: NativeBottomTabsLazy,
168+
name: 'Native Bottom Tabs with Lazy',
169+
},
159170
{ component: NativeBottomTabs, name: 'Native Bottom Tabs' },
160171
{ component: JSBottomTabs, name: 'JS Bottom Tabs' },
161172
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import TabView, { SceneMap } from 'react-native-bottom-tabs';
2+
import { useState } from 'react';
3+
import { Article } from '../Screens/Article';
4+
import { Albums } from '../Screens/Albums';
5+
import { Contacts } from '../Screens/Contacts';
6+
7+
const renderScene = SceneMap({
8+
article: Article,
9+
albums: Albums,
10+
contacts: Contacts,
11+
});
12+
13+
export default function LazyTabs() {
14+
const [index, setIndex] = useState(0);
15+
const [routes] = useState([
16+
{
17+
key: 'article',
18+
title: 'Article',
19+
focusedIcon: require('../../assets/icons/article_dark.png'),
20+
unfocusedIcon: require('../../assets/icons/chat_dark.png'),
21+
badge: '!',
22+
testID: 'articleTestID',
23+
},
24+
{
25+
key: 'albums',
26+
title: 'Albums',
27+
focusedIcon: require('../../assets/icons/grid_dark.png'),
28+
badge: '5',
29+
testID: 'albumsTestID',
30+
lazy: false,
31+
},
32+
{
33+
key: 'contacts',
34+
focusedIcon: require('../../assets/icons/person_dark.png'),
35+
title: 'Contacts',
36+
testID: 'contactsTestID',
37+
},
38+
]);
39+
40+
return (
41+
<TabView
42+
navigationState={{ index, routes }}
43+
onIndexChange={setIndex}
44+
renderScene={renderScene}
45+
/>
46+
);
47+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Article } from '../Screens/Article';
2+
import { Albums } from '../Screens/Albums';
3+
import { Contacts } from '../Screens/Contacts';
4+
import { Chat } from '../Screens/Chat';
5+
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
6+
7+
const Tab = createNativeBottomTabNavigator();
8+
9+
export default function NativeBottomTabsLazy() {
10+
return (
11+
<Tab.Navigator>
12+
<Tab.Screen
13+
name="Article"
14+
component={Article}
15+
options={{
16+
tabBarIcon: () => require('../../assets/icons/article_dark.png'),
17+
}}
18+
/>
19+
<Tab.Screen
20+
name="Albums"
21+
component={Albums}
22+
options={{
23+
tabBarIcon: () => require('../../assets/icons/grid_dark.png'),
24+
lazy: false,
25+
}}
26+
/>
27+
<Tab.Screen
28+
name="Contacts"
29+
component={Contacts}
30+
options={{
31+
tabBarIcon: () => require('../../assets/icons/person_dark.png'),
32+
}}
33+
/>
34+
<Tab.Screen
35+
name="Chat"
36+
component={Chat}
37+
options={{
38+
tabBarIcon: () => require('../../assets/icons/chat_dark.png'),
39+
}}
40+
/>
41+
</Tab.Navigator>
42+
);
43+
}

apps/example/src/Examples/TintColors.tsx

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Article } from '../Screens/Article';
44
import { Albums } from '../Screens/Albums';
55
import { Contacts } from '../Screens/Contacts';
66
import { Chat } from '../Screens/Chat';
7+
import { Button, Platform, StyleSheet, View } from 'react-native';
78

89
const renderScene = SceneMap({
910
article: Article,
@@ -12,8 +13,11 @@ const renderScene = SceneMap({
1213
chat: Chat,
1314
});
1415

16+
const isAndroid = Platform.OS === 'android';
17+
1518
export default function TintColorsExample() {
1619
const [index, setIndex] = useState(0);
20+
const [bakedTintColors, setBakedTintColors] = useState(false);
1721
const [routes] = useState([
1822
{
1923
key: 'article',
@@ -31,9 +35,11 @@ export default function TintColorsExample() {
3135
},
3236
{
3337
key: 'contacts',
34-
focusedIcon: require('../../assets/icons/person_dark.png'),
38+
focusedIcon: isAndroid
39+
? require('../../assets/icons/person_dark.png')
40+
: { sfSymbol: 'person.fill' },
3541
title: 'Contacts',
36-
activeTintColor: 'yellow',
42+
activeTintColor: 'blue',
3743
},
3844
{
3945
key: 'chat',
@@ -45,14 +51,32 @@ export default function TintColorsExample() {
4551
]);
4652

4753
return (
48-
<TabView
49-
sidebarAdaptable
50-
navigationState={{ index, routes }}
51-
onIndexChange={setIndex}
52-
renderScene={renderScene}
53-
tabBarActiveTintColor="red"
54-
tabBarInactiveTintColor="orange"
55-
scrollEdgeAppearance="default"
56-
/>
54+
<View style={styles.container}>
55+
<View style={styles.controls}>
56+
<Button
57+
title={`${bakedTintColors ? 'Disable' : 'Enable'} Experimental Baked Tint Colors`}
58+
onPress={() => setBakedTintColors((value) => !value)}
59+
/>
60+
</View>
61+
<TabView
62+
sidebarAdaptable
63+
navigationState={{ index, routes }}
64+
onIndexChange={setIndex}
65+
renderScene={renderScene}
66+
tabBarActiveTintColor="red"
67+
tabBarInactiveTintColor="orange"
68+
experimental_bakedTintColors={bakedTintColors}
69+
scrollEdgeAppearance="default"
70+
/>
71+
</View>
5772
);
5873
}
74+
75+
const styles = StyleSheet.create({
76+
container: {
77+
flex: 1,
78+
},
79+
controls: {
80+
padding: 12,
81+
},
82+
});

docs/docs/docs/guides/standalone-usage.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,24 @@ Color for inactive tabs.
180180

181181
- Type: `ColorValue`
182182

183+
:::note
184+
On iOS >= 26 (Liquid Glass), this prop is ignored.
185+
:::
186+
187+
#### `experimental_bakedTintColors` <Badge text="iOS" type="info" /> <Badge text="experimental" type="danger"/>
188+
189+
Enable an experimental mode which bakes (rasterizes) the label into the icon image, allowing for better control of the tint colors on iOS 26+.
190+
191+
- Type: `boolean`
192+
193+
:::warning
194+
This feature is experimental, and might be removed in the future.
195+
196+
It has many drawbacks, such as SFSymbol icon sizes being different on label width, badges being positioned far away from the icon depending on the label width, and possibly breaking accessibility since the label will be baked inside the icon image.
197+
198+
Use with caution, only if you prioritize tint color consistency between platforms.
199+
:::
200+
183201
#### `tabBarStyle`
184202

185203
Object containing styles for the tab bar.

0 commit comments

Comments
 (0)