Hold Position of ScrollView when adding data - react-native

I am trying to make it so whenever something new is rendered into a scroll view, the scroll view will stay put and not bump up and down. Right now if a new component is rendered in, the scrollview appears to be reset to 0.
Is there a way to stop this behavior, and hold position?
Right now for the scrollview I am using:
handleScrollt(event){
this.scroll = event.nativeEvent.contentOffset.y
}
handleSizet(width, height){
if (this.scroll) {
const position = this.scroll + height - this.height
this.refs.sv.scrollTo({x: 0, y: position, animated: false})
}
this.height = height
}
<ScrollView
ref="sv"
scrollEventThrottle={16}
onScroll={this.handleScrollt.bind(this)}
onContentSizeChange={this.handleSizet.bind(this)}
The issue with this is the scrollview will render briefly, before then scrolling to the correct offset. So it seems like theres a brief splash of the top of the screen

you have to define a height for the scrollview.
If the scrollview is supposed to cover an entire View you can get the view's height by:
<View style={styles.contactView} onLayout={(event) => {
var contactViewY = event.nativeEvent.layout.y;
this.setState({contactViewY: contactViewY})
}}>
and then give it to the scrollview
<ScrollView style={[styles.contactScroller, {height: this.state.contactViewY}]}>
Bear in mind that the onLayout method is called immediately once the layout has been calculated, so if the view's height changes later, this two lines of code alone won't update it.

You should try maintainVisibleContentPosition.
Example:
<ScrollView
{...props}
maintainVisibleContentPosition={{
minIndexForVisible: 0,
}}
/>
From the docs:
maintainVisibleContentPosition
When set, the scroll view will adjust the scroll position so that the
first child that is currently visible and at or beyond
minIndexForVisible will not change position. This is useful for lists
that are loading content in both directions, e.g. a chat thread, where
new messages coming in might otherwise cause the scroll position to
jump. A value of 0 is common, but other values such as 1 can be used
to skip loading spinners or other content that should not maintain
position.

Related

How to stay in position when new messages are loaded in a ScrollView in React-Native?

I have a ScrollView containing messages (most recent messages are further) in a chat application.
I limit the number of pre-loaded messages, and I dynamically load a new batch when the Scroll View is scrolled to the top. So when new messages are loaded, the current scrollPos is at 0.
The problem is that when the new messages arrive, the scrollPos stays at 0, so the user is teleported to the oldest newly loaded message.
I have tried to deal with it by manually scrolling back down to the position using the size of the content change, but this is not satisfying as the user sees a back and forth scrolling.
Can someone think of a way to do this so that the user does not see any change when the new messages appear an can simply gradually scroll up to see them.
I found a way to do it.
The idea comes from the Invertible Scroll View component: https://github.com/expo/react-native-invertible-scroll-view
I didn't use the component but implemented the idea directly on the Scroll View to have minimal changes in my code.
To explain, we translate vertically the Scroll View using the style of the Scroll View and transform: [{ scaleY: -1 }]. We do the same for the children. Then, we revert the order of the messages.
In that setup, the scrollPos() measures from the visual bottom. To trigger the loading of the new messagges, now I use
const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 20;
return layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom;
};
The trick is that now, when the new messages appear on top, you have nothing to do as the distance from the user's point of view to the bottom does not change.
You can use the onContentSizeChange prop to scroll to the bottom anytime it detects a change in content size.
The scrollToEnd function may differ depending on RN version.
<ScrollView
...
onContentSizeChange={() => this.scrollView.scrollToEnd({animated: true})}>
</ScrollView>
Ref: https://reactnative.dev/docs/scrollview#scrolltoend

Why aren't all items displayed in the ScrollView?

I have a Flatlist inside a ScrollView with 100 items, but no matter what height I give to the ScrollView or Flatlist, there are always just 18 items displayed on my Phone. After that the slider of the ScrollView is still scrolling down, but the list ends after 18 items. I want to be able to scroll through the complete list of items with the ScrollView containing the FlatList. I am using a Samsung Galaxy S8+ as a test device.
Here is a snack of the problem: https://snack.expo.io/#christophhummler/stickyheaderscrollscreens
Thanks for your help :)
I think it has to do with the scrollEnabled={false}, set it to true or remove the prop altogether otherwise it may be your todos array
Edit
Since you want to create a sticky header you must set the height to be the device height of screen + height of header, change styles.scrollView to :
scrollView: {
height: height + 200,
}
also on android there is an issue of nesting scrollviews, to enable a nested scrollview add the nestedScrollEnabled prop:
<FlatList
...
nestedScrollEnabled={true}
/>
You will have to implement logic to enable the Flatlist scroll view when the specific threshholds are met. It works at the moment in conjunction with scrollEnabled={true}
i hope this helps

How do I make Scrollview auto scroll when the content size changes in React Native

I currently have a vertical ScrollView that has many expandable cards inside. When one of the cards expands, its height will increase by Y, and therefore the ScrollView content size's height will increase by Y.
I want to make the ScrollView automatically scroll by Y when the content size increases, and scroll by -Y when the content size decreases. How should I achieve this?
You can use "onContentSizeChange", this will return new height after change your view. It's work for me.
<ScrollView
ref={scrollView => {
this.scrollView = scrollView;
}}
onContentSizeChange={(contentWidth, contentHeight) => {
console.log('contentHeight :==> \n', contentHeight);
this.scrollView.scrollTo({
x: 0,
y: contentHeight + YOUR_VIEW_HEIGHT - SCREEN_HEIGTH,
animated: true,
});
}}
>
...
YOUR_VIEW_HEIGHT will get from View's onLayout method.

Scrollview and child with flex: 1

Is it possible to have a layout which is wrapped with ScrollView:
<ScrollView>
<View>dynamic height</View>
<View>flex with minHeight</View>
<View>static height</View>
</ScrollView>
and meets below prerequisites:
Height of the first View is dynamic (depends on text length). Height of the third View is static (always three buttons inside). Rest of the screen should be filled with View with map, but the minimal height is set to 250.
Now the tricky part: if there is a lot of text in first View, so that map doesn't fit, a scroll should appear. I couldn't achieve that. I was trying to give the map View flex: 1 and minHeight: 250, but it's not rendered at all.
Solution
Ok, I've found a way to fix it. In first render, I get screen height (Dimensions) and height of first and third view (onLayout). Then, I calculate the height of second view (screenHeight - view1 - view3 - naviagtionHeight) and forceUpdate() to re-render it.
Add contentContainerStyle={{flexGrow: 1}} prop to ScrollView and its children's flex:1 would work. So you don't need to calculate it manually.

How do you prevent over-scrolling using react-native ScrollView.scrollTo()?

After the user does some action I want to scroll to a particular section within a ScrollView, which I'm doing using this code:
this.refs.detailsView.measure((x, y, width, height, pageX, pageY) => {
this.refs.homeScrollView.scrollTo({ x: 0, y: pageY - 64, animated: true });
});
The problem is the "detailsView" View is near the bottom of the ScrollView and on the largest iPhones, scrollTo() ends up scrolling past the bottom of ScrollViews "natural" max scroll point and you can see the ScrollViews background colour (dary grey) as though you over-scrolled using touch gestures...the components in the ScrollView have the green background colour.
Does anyone have a suggestion for how to prevent this "over-scrolling" when using the ScrollView.scrollTo() function?
react-native: v0.36
This disables the scrollview from bouncing past its limit
bounces={false}