How do I make Scrollview auto scroll when the content size changes in React Native - 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.

Related

Hold Position of ScrollView when adding data

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.

Titanium/ Alloy: Reapply styles on when screen is resized

I have some basic styles like:
"#foo": {
backgroundColor: '#ff0000', // red
}
"#foo[if=Alloy.Globals.isSmallHeight]": {
backgroundColor: '#0000ff', // blue
}
in alloy.js I have the following:
function pxToDp(px){
return px / (Titanium.Platform.displayCaps.dpi / 160);
}
var screenWidth;
var screenHeight;
if (Ti.Platform.name == "android") {
screenWidth = pxToDp(Ti.Platform.displayCaps.platformWidth);
screenHeight = pxToDp(Ti.Platform.displayCaps.platformHeight);
} else {
screenWidth = Ti.Platform.displayCaps.platformWidth;
screenHidth = Ti.Platform.displayCaps.platformHeight;
}
Alloy.Globals.isSmallHeight= screenHeight <= 545;
This basically means the background colour of #foo is blue if the screen height is less than or equal to 545dp, and red otherwise.
This usually works fine. However, there are times when the height of the screen can change during run-time. For example:
Screen Orientation Change
Multi window (on Android)
Adjusting the splitter position while in multi window mode (Android)
The issue with this is that the styles are not re-applied to take into account the new screen width and screen height.
For example, let's say there is a screen of size 600dp x 300dp in the portrait position. #foo will correctly have a background colour of red.
However, if the orientation changes, the screen size is now: 300dp x 600dp, but the #foo does not re-check the height a background colour, and thus is still red instead of blue.
A similar issue occurs when going into split screen.
Therefore my question is, how can I reapply styles when the screen dimensions changes?
Have a look at the dynamic styles section in the documentation
http://docs.appcelerator.com/platform/latest/#!/guide/Dynamic_Styling
http://docs.appcelerator.com/platform/latest/#!/guide/Dynamic_Styles
it will give you a basic idea on how to create style at runtime and apply them. You could do this inside the orientation change event

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}

How to dynamically set height of view based on its width by keeping aspect ratio?

I want to have a view whose width is responsive to its container width while keeping its aspect ratio.
I don't want to hard code width or height, but keep it dynamic and responsive so that it can be reused in multiple situations as a reusable component.
I hope it works similar to the following code:
<View style={{
alignSelf: 'stretch',
aspectRatio: 1.5
}} />
It should fill its container's width and set its own height dynamically based on its width.
Is this possible in react-native with flexbox?
As Hendy Irawan commented, React Native added aspectRatio property to StyleSheet.
https://facebook.github.io/react-native/docs/layout-props.html#aspectratio
From the document:
aspectRatio?: number
Aspect ratio control the size of the undefined dimension of a node.
Aspect ratio is a non-standard property only available in react native
and not CSS.
On a node with a set width/height aspect ratio control the size of the
unset dimension On a node with a set flex basis aspect ratio controls
the size of the node in the cross axis if unset On a node with a
measure function aspect ratio works as though the measure function
measures the flex basis On a node with flex grow/shrink aspect ratio
controls the size of the node in the cross axis if unset Aspect ratio
takes min/max dimensions into account
Thus the following should work:
const styles = StyleSheet.create({
view: {
aspectRatio: 1.5,
}
});
// later in render()
<View style={styles.view} />
Yes, you can use the Dimensions API to get the window's width, and then set height programatically. I typically call the dimensions as soon as the app loads, before you need to know the width, and then just pass the object around as needed.
This could be a custom component to accomplish your use case.
import {Dimensions} from 'react-native'
...
const AspectView = ({style, aspectRatio, children, ...props}) => {
let {height, width} = Dimensions.get('window');
return (
<View style={[style, {height: width * aspectRatio}] {...props}>{children}</View>
)
}

How can I detect if the touch is over the border of a View from PanResponder?

I'm working on a Image cropper, I have somehow figure out how to move the cropper even it's not a perfect solution but it works, but now I want to respond to the border touches/moves of a given View
I'm using this module for the corpping, but actually I'm still stuck at how to respond to border touches/moves
gestureState parameter is sufficient for the task.
x0 and y0 are the top-left coordinates of the responder view, further, moveX and moveY holds the current coordinates of touch.
So moveX === x0 means current touch is at left edge.
Similarly moveY === y0 means current touch is at top edge.
For handling right and bottom edge I suggest you use onLayout in the <View> tag and assign height and width of the view to some variable or in state variable( take care of performances optimisations)
And then use it in similar way:
onPanresponderMove(evt, {x0, y0, moveX, moveY) {
...
if(moveX=== x0 || moveX === x0 + this.state._currentWidth) {
// task for left and right edge response
...
}
...
}
To get view width:
<View {...this._myResponder.panHandlers}
onLayout={ ({width, height}) => this.state._currentWidth = width } />