I have a very simple example of using a ScrollView and I cannot seem to scroll to the bottom.
The example is completely basic, I'm not doing anything special yet the last item is never fully visible.
Expo Code
import React, { Component } from "react";
import { Text, View, ScrollView, StyleSheet } from "react-native";
import { Constants } from "expo";
const data = Array.from({ length: 20 }).map((_, i) => i + 1);
export default class App extends Component {
render() {
return (
<ScrollView style={styles.container}>
{data.map(d => (
<View style={styles.view}>
<Text style={styles.text}>{d}</Text>
</View>
))}
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: Constants.statusBarHeight
},
text: {
fontWeight: "bold",
color: "#fff",
textAlign: "center",
fontSize: 28
},
view: {
padding: 10,
backgroundColor: "#018bbc"
}
});
Here is the output:
Apply padding styles to "contentContainerStyle" prop instead of "style" prop of the ScrollView.
I had just a ScrollView in my application and it was working fine. Then I added a header component at the top, and the bottom of my ScrollView was not visible any more. I tried everything and nothing worked. Until I found a solution after an hour of trying every crazy thing and here it is. I fixed the issue by adding paddingBottom as contentContainerStyle to my ScrollView.
<ScrollView contentContainerStyle={{paddingBottom: 60}} >
{this.renderItems()}
</ScrollView>
set flexGrow: 1 to contentContainerStyle of your ScrollView
contentContainerStyle={{ flexGrow: 1 }}
Modify your render() method and wrap the ScrollView inside a View container, and set the paddingTop to that View container:
render() {
return (
<View style={ styles.container}>
<ScrollView >
{data.map(d => <View style={styles.view}><Text style={styles.text}>{d}</Text></View>)}
</ScrollView>
</View>
);
}
I fixed mine by setting Scrollview's contentInset bottom property to a value just enough for me to see bottommost part of my content (a button in my case).
Example:
<ScrollView
contentInset={{bottom: 80}}>
{{ content }}
</ScrollView>
I have this issue and fixed it. To solve it:
flex:1 means “expand it to its parent size”, if parent have a 0 height, it won’t work
flex:-1 means “shrink it to fit its children size”, if child height set to flex:1 (depends on parent) -> it wont’ work -> you ask me? I ask you back. loop.
scrollview have two view in it -> a {flex:1} outer view wrapped a {flex:-1} inner view. when inner view taller than outer view, that’s when scroll happen.
It also means outer view height depends on scrollview parent, inner view depends on children. If you cannot give a height to its outer view, the outer view will become 0 height. You cannot scroll inner view with a 0 height outer view, it will always bounce back to it origin position once your finger release.
wrap it with a <View style={{flex:1}}> may or may not solve your problems because it means the scrollview’s parent expand itself to fit its parent,height then depends on it parent. (the grand parent of your scrollview). So if your grand parent have no height defined, it fail.
The follow example won’t works
<View style={{height:300,width:200}}>
<View> //<-this break
<View {{flex:1}}>
<ScrollView>{contents}</ScrollView>
</View>
</View>
</View>
and this work:
var {Windowheight, width} = Dimensions.get('window');
<View style={{height:Windowheight}}>
<View style={{flex:1}}>
<ScrollView>{contents}</ScrollView>
<View>
</View>
this also work:
var {windowHeight, windowWidth} = Dimensions.get('window');
<View style={{height:windowHeight}}>
<Toolbar or other component with fixed height>
<ScrollView style={{flex:1}}>{contents}</ScrollView>
<Footer or other component with fixed height>
</View>
Conclusion, make sure your scrollview height determinable both from outside and inside. If weird things happen trace the ancestor until you find a height or simply define a solid one within its traceable ancestor without rely on flex:1 Some third party component wrap things with a <View> which breaks things.
Added height style for ScrollView.
height:'100%', width:'100%'
Set the child components height without using % e.g make minHeight: '10%' -> minHeight: 10
Related
I'm trying to make an element stay at the top of the screen at all times, vene when scrolling. I found out that position: 'sticky' does this for regular html. I was wondering if there was something I could add to my css to make the element stick to one place while scrolling using react-native.
On ScrollView there is a prop called stickyHeaderIndices. Pass the index of the component that you want to be sticky in this prop. For example, if you want to sticky the header to be sticky from the below code, you have to just pass 1 in stickyHeaderIndices props like this :
import React from 'react';
import { View, ScrollView, StyleSheet, Text } from 'react-native';
const App = () => {
return (
<ScrollView stickyHeaderIndices={[1]}>
<View>
<Text style={styles.overline}>
Overline
</Text>
</View>
<View>
<Text style={styles.header}>
Header
</Text>
</View>
{/* Your others component goes here */}
</ScrollView>
)
}
const styles = StyleSheet.create({
overline: {
fontSize: 12,
fontWeight: '100'
},
header: {
fontSize: 24,
fontWeight: 'bold'
},
spacer: {
height: 10,
}
});
export default App;
This prop accepts the array, so you can also set multiple sticky components by passing the index of those all components.
Since I heard this from RN docs 'Never put a flat list inside scroll view wrapper'.
But I have one huge page to make and it requires a scroll itself, and there are children flat lists as well.
Of course, this causes a horrible scroll lag user experience, so there is anyone experienced the same kind of problem as me? What was a solution to this?
This is my snippet.
<View style={{ flex: 1 }}>
<ScrollView
ref={c => this.detailScrollView = c}
style={{ flex: 1, backgroundColor: "#f8faf9" }}
>
<FlatList1 goodsDetails />
<FlatList2 goodsPriceSelector />
<FlatList3 bestreviews />
<FlatList4 sellerGoods />
<FlatList5 sameCategoryGoods />
</ScrollView>
</View>
edit:
Today, thanks to #MaieonBrix I replaced my childeren flatlists to SectionLists, could feel a bit of improvement. In test android build it still janky, but I think I'm on the right track. I will keep this updated.
{descImgs && !!descImgs.length ?
<SectionList
sections={descImgs.slice(0,2)}
ref={c => this.goodsDesc = c}
style={{ marginTop: 22, marginBottom: 22 }}
keyExtractor={item => item}
renderItem={function ({ img }) {
return (
<View style={{ alignSelf: "center" }}>
{img.includes("https://") ? (
<FastImage
resizeMode={FastImage.resizeMode.contain}
style={{ height: width, width: width, }}
source={{ uri: img }}
/>
) : <TextNoScailing>상세이미지가 없습니다.</TextNoScailing>}
</View>
);
}.bind(this)}
/>
Well thats true.Flatlist would not work fine under scrollview in android because both using same gestures in same direction.Possible solutions
1) You can define your custom gestures using pan responder library "which is a bit tough for a biggner"
2) Disable/Enable scroll using conditions.e-g: When you're touching a flatlist then disable scroll of scrollView Similarly when on scrollView then disable for Flatlist.
The above solution is for android only because for iOS it will work fine automatically.Similarly you've to provide horizontal margin for flatlist container .Becuse only then use can touch the scroll view container near the boundries.
Hope it clears
I am trying to make a simple application/game to learn and practice React Native. Currently, I am trying to create a sticky TouchableNativeFeedback that moves with the screen as I am using the ScrollView.
The idea is that left half of the screen would move the character to the left and the right half of the screen would move it to the right. I want these controls to be fixed in the display and not move.
This is how it starts
This is after moving the scrollView a bit
I've initially tried to change value of style.left as I scrolled but that doesn't seem to be a good/stable solution.
Here is the current code:
render() {
return (
<ScrollView
//onScroll={this._onScroll}
ref={(scrollView) => { this.scrollView = scrollView; }}
style={styles.container}
horizontal= {true}
snapToAlignment={"center"}
>
<View style={styles.wrapperView}>
<ImageBackground
style={styles.container}
source={require('../images/second.png')}
>
<TouchableNativeFeedback onPressIn={this._onPressButton} onPressOut={this._onPressButton}>
<View style={
{
width: width/2,
height: '100%',
position: 'absolute',
left: this.state.touchableLeft,
backgroundColor: 'red',
}
}>
</View>
</TouchableNativeFeedback>
... (code about the character)
</ImageBackground>
</View>
</ScrollView>
);
}
and the styles code
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
},
wrapperView:{
width: width*3 + 300,
},
});
and just to have it as a reference, this is what I originally tried:
_onScroll = (event) => {
this.setState( {
touchableLeft: this.state.touchableLeft + event.nativeEvent.contentOffset.x
} )
}
I've looked at the following questions and articles but I couldn't really get to a solution that would help me. Usually people use flex to make their headers sticky above a ScrollView and that is incredibly handy but in this situation I am unsure about how to continue. Articles/Questions:
How to Get ListView Section Header to Stick
http://docs.nativebase.io/docs/examples/StickyHeaderExample.html
Sticky Component inside scrollview
What solved my problem was to take the TouchableNativeFeedback outside of the class. The class in the question was called Background and it was rendered in the class called App.
<View style={styles.container}>
<Background />
<TouchableNativeFeedback onPressIn={this._onPressButton} onPressOut={this._onPressButton}>
<View style={
{
width: '50%',
height: '100%',
position: 'absolute',
left:0,
//left: this.state.touchableLeft,
//backgroundColor: 'red',
}
}>
</View>
</TouchableNativeFeedback>
</View>
As you can see once I moved it to here, it was positioned right where I wanted, even if I move the screeen. What would be good practice, is to take it to another component class and then just call an instance of it.
Thanks to John Ruddell for the help while coming up with the solution
I'm using the react-native-swiper, but I didn't find any solution for my problem in the docs. Currently it's show me one picture at once. But I want one and half, and always slide one by one. Is it possible somehow, because the slides have a strict width.
A design what I would achive:
There is my current code:
class Featured extends Component {
/**
* Render the featured box
*/
renderFeatured() {
return this.props.featured.data.items.map(object => (
<View style={styles.slide} key={object.id}>
<FeaturedBox
id={object.id}
image={Helpers.getPrimaryImage(object.images)}
text={object.name}
/>
</View>
)
);
}
render() {
if (Helpers.isObjectEmpty(this.props.featured)) {
return (
<View />
);
}
return (
<View>
<Swiper
style={styles.wrapper}
height={150}
horizontal={false}
showsPagination={false}
>
{this.renderFeatured()}
</Swiper>
</View>
);
}
}
const styles = StyleSheet.create({
wrapper: {
},
slide: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
});
Basically the FeaturedBox is just an image.
I found the solution, and change the Swiper to Carousel.
I had the same problem with you.
I tried many times, eventually solved this problem and I created npm package for these use cases.
First of all, You can solve this error by adding specific width in the images via your device.
If your use cases is adding multiple images, then you can solve that easily by using method below.
Hope react-native-image-swiper helps you.
usage
import {Dimensions} from 'react-native';
import ImagesSwiper from "react-native-image-swiper";
const { width, height } = Dimensions.get("window");
<ImagesSwiper width={width} height={height-400} />
I suggest you to use react-native-snap-carousel
follow below link for more details
https://www.npmjs.com/package/react-native-snap-carousel
You can do this via following code;
<Swiper removeClippedSubviews={false}>
<View style={{width: screenWidth - 100}}></View>
<View style={{marginLeft: - 100}}> </View>
</Swiper>
I'm having a problem with a WebView in my React Native app. When no height for the WebView is specified, it defaults to about half the screen height (iPhone 6S). However, when I set a height with the help of Dimensions, it displays fine, but only the original half is interactive – ie. can only scroll using the top half of the screen.
Here are the main parts of my current code:
import React, { Component } from 'react';
import {
...
Dimensions,
...
WebView,
} from 'react-native';
let ScreenHeight = Dimensions.get("window").height;
let ScreenWidth = Dimensions.get("window").width;
class App extends Component {
render() {
return (
<View style={styles.container}>
...
<WebView
source={{uri: 'http://foo.com'}}
style={{height: ScreenHeight, width: ScreenWidth}}
/>
...
</View>
);
}
}
...
const styles = StyleSheet.create({
container: {
backgroundColor: '#bbb',
flex: 1,
},
...
});
AppRegistry.registerComponent('myApp', () => App);
I look forward to any help that can be offered :)
It's possible that an invisible view from your render function is being drawn on the bottom half of the screen.
With out seeing the rest of the render method I couldn't tell, still worth checking.
Thanks for the answers, guys. I've fixed my issue now, and it seems to be such an easy fix. I basically wrapped the WebView in it's own View container. I'm not sure whether it wasn't working because it had sibling elements (ie. NavBar and TabBarIOS – which I left off my previous snippet – sorry!), but the WebView now works great.
Here is my new render function:
render() {
return (
<View style={styles.container}>
<NavBar>
<View>
<WebView
source={{uri: 'http://foo.com'}}
style={{height: ScreenHeight, width: ScreenWidth}}
/>
</View>
<TabBarIOS>
...
</TabBarIOS>
</View>
);
}
Try adding flex style to WebView. Your code will look like this:
<View style={styles.container}>
...
<WebView
source={{uri: 'http://foo.com'}}
style={{flex:1}}
/>
...
</View>