React-native-tab-view: `renderLabel` and `renderIcon` wrap or cut off text/icon if unfocused text/icon is smaller than focused text

Created on 28 Mar 2020  路  7Comments  路  Source: satya164/react-native-tab-view

Current behaviour

If the Text component returned by renderLabel when focused is false is smaller (in width) than the Text returned when focused is true, then the Text component for focused tab gets wrapped.

Similar issue exists with renderIcon. If the Icon returned by renderIcon when focused is false is smaller (in width and height) than the Icon returned when focused is true, then the Icon for focused tab gets cut off.

Neither of the issues happen when the focused one is smaller than the unfocused one. Infact, that causes another issue where the box designated for focused version is larger than it needs and it gets offcenter.

It seems that in both the cases, the text is drawn within a box of width of the unfocused component.

Expected behaviour

The icon and text should get enough space to be drawn.

Code sample

import * as React from 'react';
import { View, StyleSheet, Dimensions, Text } from 'react-native';
import { TabView, TabBar, SceneMap } from 'react-native-tab-view';

const FirstRoute = () => (
  <View style={[styles.scene, { backgroundColor: '#ff4081' }]} />
);

const SecondRoute = () => (
  <View style={[styles.scene, { backgroundColor: '#673ab7' }]} />
);
function renderLabel({ route, focused, color }) {
  return <Text style={{ color }}>{focused ? route.title : ' '}</Text>;
}
const initialLayout = { width: Dimensions.get('window').width };

export default function App() {
  const [index, setIndex] = React.useState(0);
  const [routes] = React.useState([
    { key: 'first', title: 'First' },
    { key: 'second', title: 'Second' },
  ]);

  const renderScene = SceneMap({
    first: FirstRoute,
    second: SecondRoute,
  });

  return (
    <TabView
      navigationState={{ index, routes }}
      renderScene={renderScene}
      onIndexChange={setIndex}
      initialLayout={initialLayout}
      renderTabBar={props => <TabBar {...props} renderLabel={renderLabel} />}
    />
  );
}

const styles = StyleSheet.create({
  scene: {
    flex: 1,
  },
});

Above code: https://snack.expo.io/HySYUg6UL

Changing renderLabel to the following code will display the offcenter issue

function renderLabel({ route, focused, color }) {
  return <Text style={{ color }}>{focused ? 'x' : 'a bit long text'}</Text>;
}

Code with above modification: https://snack.expo.io/B1qdDg6UU

Screenshots (if applicable)

Wrapping issue

Wrapping Issue

Offcenter issue

Offcenter Issue

What have you tried


My current workaround for hiding labels on inactive tabs (I only want to show icons) is to set text color as color of tab bar. this isn't exactly perfect since the text becomes partially visible during a tap and isn't useful in all cases (e.g. if you want to show an abbrevation in non focused cases).

Your Environment

| software | version
| ---------------------------- | -------
| android | 9.0
| react-native | 3.2.1
| react-native-tab-view | 2.13.0
| react-native-gesture-handler | 1.6.0
| react-native-reanimated | 1.7.0
| node | 13.8.0
| yarn | 1.22.0

bug

Most helpful comment

So I found a better solution, leaving the bold text rendered on the screen from the start, along with the main text, however transparent and with a small size.
The problem I see is that the lib does not render again when changing the font size, which causes a mismatch with the layout, some memoization or pure components.

My solution:

renderLabel={({route, focused}) => {
  return (
    <View>
      <Text style={{fontWeight: focused ? 'bold' : 'normal'}}>
        {route.title}
      </Text>
      <View style={{height: 1}}>
        <Text
          style={{
            fontWeight: 'bold',
            color: 'transparent',
          }}>
          {route.title}
        </Text>
      </View>
    </View>
  );
}}

All 7 comments

me too

I have a very similar issue. My render label function bolds focused tab. This causes an issue on android where the last character gets wrapped to the the next line when the tab is focused.

@shockoe-hatch that is definitely the same issue! Making the text bold will increase the width of the text so since width of focused text will be larger than unfocused text, it will wrap.

I'm still waiting for this issue to be fixed.

For anyone looking for a quick hack to work around this issue, I have setup my renderLabel to render two Text components, one is the "largest" size and is essentially hidden by being the same color as the background color and the "smallest" is then positioned absolute over it.

            renderLabel={({ route, focused }) => (
              <View>
                <Text style={[...styles.tabBarLabelActive, { color: 'white' }]}>
                  {route.title?.toUpperCase()}
                </Text>
                <Text
                  style={[
                    ...(focused
                      ? styles.tabBarLabelActive
                      : styles.tabBarLabel),
                    { position: 'absolute' },
                  ]}
                >
                  {route.title?.toUpperCase()}
                </Text>
              </View>
            )}

@MPiccinato this worked for me! Thanks!

So I found a better solution, leaving the bold text rendered on the screen from the start, along with the main text, however transparent and with a small size.
The problem I see is that the lib does not render again when changing the font size, which causes a mismatch with the layout, some memoization or pure components.

My solution:

renderLabel={({route, focused}) => {
  return (
    <View>
      <Text style={{fontWeight: focused ? 'bold' : 'normal'}}>
        {route.title}
      </Text>
      <View style={{height: 1}}>
        <Text
          style={{
            fontWeight: 'bold',
            color: 'transparent',
          }}>
          {route.title}
        </Text>
      </View>
    </View>
  );
}}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ahmedrowaihi picture ahmedrowaihi  路  3Comments

karthikeyansundaram2 picture karthikeyansundaram2  路  3Comments

itzsaga picture itzsaga  路  3Comments

moerabaya picture moerabaya  路  4Comments

nastarfan picture nastarfan  路  3Comments