16

I am trying to make my content start 100 px from the top in React Native. I have tried with

const OFFSET = 100
const ScrollViewTest = (props) => (
  <ScrollView
    contentInset={{ top: OFFSET }}
    contentOffset={{ y: OFFSET }}
  >
    <Text>Test</Text>
  </ScrollView>
)

But when I render the screen, it starts from 0 px, but if I scroll a little, it will scroll to 100px from the top and stay there.

So it seems React Native doen't trigger the contentOffset and contentInset properties on initialization.

How can I fix this? I have also tried setting automaticallyAdjustContentInsets={false} with no changes.

Also, it seems these properties are for iOS only. Are there any similar properties for Android?

Jamgreen
  • 10,329
  • 29
  • 113
  • 224

5 Answers5

11

You should use the contentContainerStyle1 property with a marginTop on your ScrollView.

By using this property it will apply the container wrapping your children (which I believe is what you want in this case) and with the additional benefit of working on both iOS and Android.

I.e.

const OFFSET = 100
const ScrollViewTest = (props) => (
  <ScrollView
    contentContainerStyle={{ marginTop: OFFSET }}
  >
    <Text>Test</Text>
  </ScrollView>
)
Peter Theill
  • 3,117
  • 2
  • 27
  • 29
  • This solution works most of the times, but here are some cases where it is not a good idea: 1) Having a transparent Header where the content should scroll underneath. 2) When working with RefreshControl. 1) Can be solved with replacing `contentContainerStyle` with `style={{paddingTop: headerHeight}}` 2) Needs to be solved with Inset & Offset like @Jamgreen suggested. However, I have the same problem as he has. – Mika Sep 09 '20 at 12:05
  • This does add the content offset but it also cuts off the end of the scroll view by the same margin – Ken Oct 09 '22 at 19:33
3
    componentDidMount(){
        if(Platform.OS==='ios'){
          this.refs._scrollView.scrollToOffset({animated:false,offset:-OFFSET});
        }
    }

    render(){
        return(
            <ScrollView
                ref="_scrollView"
                contentInset={{top: OFFSET}}
            ...>
               ...
           </ScrollView>
        )
    }
James Kuang
  • 121
  • 3
2

When I try to call scrollTo from componentDidMount method it does not scroll. I have to use workaround with setTimeout to make it work:

componentDidMount() {
  setTimeout(() => this.refs._scrollView.scrollTo({ x: 100, y: 0 }) , 0);
}
yuanwy
  • 31
  • 2
2

As I wrote in the comments to Peter Theill's solution, the proposed solutions here do not work in 100% of the cases, e.g. for one of the following scenarios:

  1. You have a transparent Header where you want the content to scroll underneath (e.g. a blurred header).

You could solve this with Peter Theill's solution and replacing contentContainerStyle with style={{paddingTop: headerHeight}}.

But this won't help you if you have also the following typical scenario:

  1. You want to use RefreshControl. It will be displayed behind the header and there is no way to position it differently on iOS. Therefore, you need to use contentOffset & contentInset as Jamgreen proposed. However, I had kind of similar issues with initial rendering of offset. I solved it in my case with the prop automaticallyAdjustContentInsets={false}, so I would recommend to try this!

Here is the complete solution (for android & iOS) in my scenario:

<ScrollView
    style={{paddingTop: Platform.select({android: headerHeight, ios: 0})}}
    scrollIndicatorInsets={{ right: 1 }}
    ref={scrollViewRef}
    contentInset={{ top: headerHeight}}
    contentOffset={{ x: 0, y: Platform.select({android: 0, ios: -headerHeight})}}
    automaticallyAdjustContentInsets={false}
    refreshControl={<RefreshControl
        refreshing={refresh}
        onRefresh={() => {
            setRefresh(true);
            setTimeout(() => setRefresh(false), 10000);
        }}
        colors={[inputPlaceholderGray]}
        tintColor={inputPlaceholderGray}
        progressViewOffset={headerHeight}
    />}
>
    {CONTENT}
</ScrollView>

Hope it still helps people although the issue is already old.

Mika
  • 534
  • 5
  • 14
0

padding-like effect:

const OFFSET = 100
const ScrollViewTest = (props) => (
    <ScrollView>
        <View style={{ height: OFFSET }} />
        <Text>Test</Text>
    </ScrollView>
)

header-like effect:

const OFFSET = 100
const ScrollViewTest = (props) => (
    <View style={{ paddingTop: OFFSET }}>
        <ScrollView>
            <Text>Test</Text>
        </ScrollView>
    </View >
)
Alexander Vitanov
  • 4,074
  • 2
  • 19
  • 22
  • But if, instead of `..` have a `` with `RefreshControl`, the refresh button will be before the padding-like effect instead of just above the list, so I cannot accomplish the same :( – Jamgreen Sep 26 '17 at 04:11
  • Interesting what overflow: “hidden” will do in this case – Alexander Vitanov Sep 26 '17 at 09:35