PedroBern / react-native-collapsible-tab-view

A cross-platform Collapsible Tab View component for React Native
MIT License
815 stars 161 forks source link

Tab content is still scrollable even if there is no content to be scrolled #328

Closed fikkatra closed 1 year ago

fikkatra commented 1 year ago

I'm using a ScrollView that contains dynamic content. Everything works really well when there is more content than the screen can show: the content is scrollable and the header moves out of view. However, when the content is small enough so that it is visible on the screen even without scrolling, the tab content is still scrollable, even though there is no need for scrolling.

This behaviour seems to be by design, but it's not always suitable, as it creates additional 'space' underneath the content in order to make it scrollable. This prevents aligning content vertically in the flex container of the tab content. Vertical aligned content is shown too 'low', because the flex container is higher than is strictly necessary.

I've added a dashed border to the tab content to indicate its size.

Observed behaviour: collapsible-tab-view

Desired behaviour: image

Is there any way to achieve this behaviour, using the API, hooks,...?

Use case: showing empty states, a vertically aligned loading indicator,...

Relevant code:

    <Tabs.Container renderHeader={Header}>
      <Tabs.Tab name="A">
        <Tabs.ScrollView></Tabs.ScrollView>
      </Tabs.Tab>
      <Tabs.Tab name="B">
        <Tabs.ScrollView>
          <View
            style={{
              flex: 1,
              alignItems: "center",
              justifyContent: "center",
              borderStyle: "dashed",
              borderColor: "#ddd",
              borderWidth: 4,
              margin: 10,
              padding: 10,
            }}
          >
            <Text style={{ textAlign: "center" }}>
              This text should be vertically centered within the tab content.
              The tab content should not be scrollable.
            </Text>
          </View>
        </Tabs.ScrollView>
      </Tabs.Tab>
    </Tabs.Container>
andreialecu commented 1 year ago

Regarding the issue at hand, it's essential that the content be able to scroll up by design. This is because the first tab may be scrollable, and if you collapse the header, swiping to the second tab will cause the header to be at the top, making the content start from the middle of the page and look buggy.

To better understand this, please take a look at the example app in the repository. There's a specific example that demonstrates adjusting the height of the content dynamically on scroll so that you can center your text as it scrolls up and down.

fikkatra commented 1 year ago

Thanks for the clarification, @andreialecu! This fixed my question.

For future reference, this is how to obtain center aligned tab content:

Create a custom hook:

import { useHeaderMeasurements } from 'react-native-collapsible-tab-view';
import {
  interpolate,
  useAnimatedStyle,
  useDerivedValue,
} from 'react-native-reanimated';

export function useCenterAlignedTabContent() {
  const { top, height } = useHeaderMeasurements();
  const translateY = useDerivedValue(() => {
    return interpolate(
      -top.value,
      [0, height.value || 0],
      [-(height.value || 0) / 2, 0],
    );
  }, [height]);

  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateY: translateY.value,
        },
      ],
    };
  });

  return animatedStyles;
}

Use it in the tab content:

const animatedStyles = useCenterAlignedTabContent();

<Animated.View
  style={[
    animatedStyles,
    { flex: 1, alignItems: 'center', justifyContent: 'center' },
  ]}>
  <Text>This text is now center aligned</Text>
</Animated.View>