ScrollView: Problems with height and scrolling - javascript

I have a <ScrollView />. In the documentation it says
Keep in mind that ScrollViews must have a bounded height in order to work, since they contain unbounded-height children into a bounded container (via a scroll interaction). In order to bound the height of a ScrollView, either set the height of the view directly (discouraged) or make sure all parent views have bounded height.
This is my markup:
<View
style={{
paddingLeft: 15,
paddingRight: 15,
width: '100%',
height: Dimensions.get('window').height,
alignItems: 'center'
}}>
<Foo /> // Custom Component with specific height
<ScrollView
style={{width: '100%'}}
...
{foo.map((obj, index) => {
return <View
key={index}
style={{
display: 'flex',
width: '100%',
paddingTop: 2,
paddingBottom: 2,
}}
...
</View>
})
}
</ScrollView >
</View>
In my markup, I have set the height of the parent <View /> with Dimensions.get.... But the <ScrollView /> is not scrolling all the way to the bottom.
Why is the <ScrollView /> not behaving as expected? I want to be able to scroll all the way down and see all the elements.

make shure ScrollView has flex: 1 set in style props.
Forgetting to transfer {flex: 1} down the view stack can lead to errors here, which the element inspector makes quick to debug.

Related

How to align card-images next to each other in react native

I just started with react native, I wanted to align card images next to each other (react-native-elements) but it does not work for some reason. What I want to achieve is two rows in on phones and 3 rows on tablets. But for some reason my code does not work. First the cards are aligned connected to each other no space between them and on my phone (Iphone 12) it is aligned as a column. Even though I have set flex-direction to row. could someone look at my code and tell me what is wrong?
card-image element
<View style={styles.secondPartOfDisplay}>
{categories.map((c,i) => {
return (
<View key={i} style={{width: 200, height: 150, flexDirection: 'row'}}>
<Card.Image >
<Image
style={{width:200,height:150}}
resizeMode="cover"
source={c.imageSource}
/>
</Card.Image>
</View>
)
})}
</View>
styles:
secondPartOfDisplay: {
paddingTop: 30,
display:'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: "space-between"
},
And here is the image on how it looks on phone:
This is how it looks on ipad:
If there iis no extra margin or padding that's messing with your container grid, then probably your images are not responsive according to the screen size. You would probably need to set your image height: "100%" and width: "auto"
Hope this resolves your issue.

React Native - How to make portion of TouchableOpacity not pressable

I have a Card component that displays a listing on the app's dashboard page. The whole card is wrapped in a TouchableOpacity. I want the entire card to be Pressable, which links to the listings page.
However, I want the bottom right corner of it to not be pressable, since it contains a View that has 2 icons on it (a Like button and a Message button). Each of these icons has their own onPress() that needs to happen.
Right now, if you try to click on the bottom right icons, it just triggers the whole TouchableOpacity
Here is the return statement for Card:
return (
<Container>
<Cover style={{ backgroundColor: "red" }}>
<Image
source={{
width: 80,
height: 90,
url: "https://picsum.photos/200/300",
}}
/>
</Cover>
<Content>
<Title>{props.name}</Title>
<PriceCaption>{"$" + props.price}</PriceCaption>
<View
style={{
position: "absolute",
height: 35,
width: 100,
top: 45,
right: 0,
flexDirection: "row",
alignItems: "Flex-end",
}}
>
<TouchableOpacity
style={{
...styles.ButtonBackground,
backgroundColor: null,
left: 70,
}}
onPress={() => {
launchChat();
}}
title={"message"}
>
<AntDesign name="mail" size={26} color={colours.purple} />
</TouchableOpacity>
<TouchableOpacity
style={{
...styles.ButtonBackground,
backgroundColor: null,
marginLeft: 0,
left: 10,
}}
onPress={() => {
launchChat();
}}
title={"message"}
>
<AntDesign name="heart" size={26} color={colours.purple} />
</TouchableOpacity>
</View>
</Content>
</Container>);
Is there a property, or way I can exclude a nested View from a TouchableOpacity?
Note: If you click on the Icons, they still trigger their respective onPress(), however, the entire Card still fades out, and the animation is the same as if you clicked elsewhere on the card (so user gets poor feedback on where they clicked).
I have an understanding problem in your question, but I believe that wrapping with TouchableWithoutFeedback might be a solution or pointerEvents={"none"} property. If you do a little research on the keyword TouchableWithoutFeedback component from react-native, I think you will get the result you want
I frequently place some TouchableOpacity in other TouchableOpacities. When you press on inner TO, the inner will be pressed not the parent. So you shouldn't think about making some area of parent unpressable.
I just test my sample code to be sure. As I said earlier, it should work. I have no idea what causes this problem in your case.

React Native Scroll View Question (keep track of full page scroll loaction)

I have a scroll view that contains an image that covers the entire screen. There are two buttons at the bottom of the screen, these will allow you to download or set the image as a wallpaper. The problem I'm having is I don't know which image is being looked at in the horizontal scroll view when the button is clicked.
I have knowledge of the screen size and was assuming if I could get the content offset or something like that from the currently viewed image in the view divided by the screen size could let me know what image in the array I'm looking at to perform the rest of the functionality.
unfortunately, I'm unaware of how this could be done the code is below to give a bit of context.
<ScrollView
horizontal
decelerationRate={0}
snapToInterval={width}
snapToAlignment={"center"}
style={{ height: height }}
>
{
availibleBackgrounds.map(element => (
<Image
style={{ width: width, height: height, justifyContent: 'flex-end', alignItems: 'center' }}
source={{
uri: element
}}
/>
))
}
</ScrollView>
Thanks in advance,
Chris.
You need to get the offset of the content and you can do it with adding the following prop the the ScrollView:
onMomentumScrollEnd={event => setSelectedIndex(event.nativeEvent.contentOffset.x/width)}
setSelectedIndex is a function you should create.
To add a little more closure to this question here's what I've done
calculateCurrentIndex(position) {
this.setState({
selectedIndex: {
current: Math.ceil((position / width).toFixed(2)),
}
});
}
----------------------------------------
<ScrollView
horizontal
decelerationRate={0}
snapToInterval={width}
snapToAlignment={"center"}
style={{ height: height }}
onMomentumScrollEnd={event => this.calculateCurrentIndex(event.nativeEvent.contentOffset.x)}
showsHorizontalScrollIndicator={false}
>
{
availibleBackgrounds.map((element, index) => (
<Image
key={index}
style={{ width: width, height: height, justifyContent: 'flex-end', alignItems: 'center' }}
source={{
uri: element
}}
/>
))
}
</ScrollView>

Why does React Native not offer a justify-self

I want to align an item in the primary axis. For example, I want to have a row with a few children all aligned left, and then one child aligned on the right side.
You could achieve that effect with something like "position: absolute; right: 0", but I'm wondering if theres a better way. It seems like there ought to be a justifySelf property, that only affects one child and affects its alignment on the primary axis, in the same way the alignSelf affects one child's alignment on the secondary axis.
Yet no such justifySelf seems to exist. Why is this?
It's similar to this question but not quite: How can you float: right in React Native?
I don't know React Native, but I do know flexbox!
Use the following code as a guide:
<div style="display: flex;">
<div>
I'll be on the left side
</div>
<div>
I'll be hugging the guy on the left side
</div>
<div>
I'll be hugging the guy hugging the guy on the left side
</div>
<div style="margin-left: auto;">
I'll be hugging the right side far away from those other guys
</div>
</div>
The margin set on the last child will push all other children to the left as far as their styles will allow, and push itself as far right as any other styles will allow.
You can test this out by also adding margin-right: auto; to the last child, and you will see the last child centered perfectly in the remaining space of the parent div, after the first three children take up their allotted space. This is because the competing "margin autos" will both share equally whatever space remains, since they can't cancel each other out and won't override each other.
Flex box was designed to handle margin spacing like this, so take advantage of it, as well as the other unique spacing options available under the justify-content property.
Helpful article: https://hackernoon.com/flexbox-s-best-kept-secret-bd3d892826b6
I believe you want to achieve something like this:
You can implement this by nesting views which share the same justifyContent property.
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
}}>
<View>
<View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={{width: 50, height: 50, backgroundColor: 'skyblue'}} />
</View>
<View style={{width: 50, height: 50, backgroundColor: 'steelblue'}} />
</View>
A partial answer: there's no justifySelf in React Native because there's no justify-self for flexbox in real CSS. There is a justify-self CSS property, but it doesn't do anything in flexbox layouts. You can see in the spec at https://drafts.csswg.org/css-align-3/#overview that justify-self is defined to apply to:
block-level boxes, absolutely-positioned boxes, and grid items
which notably doesn't include "flex items".
Okay, but why is this the case in CSS? MDN offers an explanation that you may or may not find satisfactory:
There is no justify-self in Flexbox
On the main axis Flexbox deals with our content as a group. The amount of space required to lay out the items is calculated, and the leftover space is then available for distribution. The justify-content property controls how that leftover space is used. Set justify-content: flex-end and the extra space is placed before the items, justify-content: space-around and it is placed either side of the item in that dimension, etc.
This means that a justify-self property does not make sense in Flexbox as we are always dealing with moving the entire group of items around.
On the cross axis align-self makes sense as we potentially have additional space in the flex container in that dimension, in which a single item can be moved to the start and end.
There's clearly some sense to this. It's always meaningful and coherent to put alignSelf: 'center' on a flex item to ask for it to be centered on the cross axis. But what would it mean to, for instance, put justifySelf: 'center' on a flex item, to ask for it to be centered on the main axis? How is that supposed to be handled if previous flex items have already filled more than half of the space along that axis?
Margins provide a fine solution for cases like yours. justifySelf, on the other hand, can't reasonably exist for flexbox, because the justifyContent values specify how to distribute flex items, and a little thought about what it would mean to instead apply them to individually specified items reveals that doing so is basically incoherent.
There is an easy way to do this without absolute positioning that works exactly how you'd expect with all items of varying heights lining up on the y-axis appropriately.
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ backgroundColor: 'green', height: 50, width: 50 }} />
<Text style={{flex: 1}}> text in here because why not!</Text>
<View style={{ backgroundColor: 'yellow', width: 30, height: 30 }} />
</View>
You can take a look at Flex Docs!
Adding flexDirection to a component's style determines the primary axis of its layout.
and then:
Adding alignItems to a component's style determines the alignment of children along the secondary axis (if the primary axis is row, then the secondary is column, and vice versa).
So your desired code will be:
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class AlignItemsBasics extends Component {
render() {
return (
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<View style={{width: 50, height: 50, backgroundColor: 'powderblue'}} />
<View style={{width: 50, height: 50, backgroundColor: 'skyblue'}} />
<View style={{width: 50, height: 50, backgroundColor: 'steelblue'}} />
</View>
);
}
};
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => AlignItemsBasics);
UPDATE
If you mean something like this image:
Then I'll suggest you this:
import React, { Component } from "react";
import { View, StyleSheet } from "react-native";
class Playground extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.boxes} />
<View style={styles.boxes} />
<View
style={[
styles.boxes,
{
backgroundColor: "crimson",
position: "absolute",
right: 0
}
]}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center"
},
boxes: {
width: 50,
height: 50,
marginLeft: 1, // to separate each box!
backgroundColor: "steelblue"
}
});
export default Playground;
As far as i know with these props, it's the best way!
I really think the best way around this is to wrap whatever you're displaying inside a <View> with position: "absolute", and also add position: "absolute" to the element you want. Then, inside the <View>, add justifyContent: "center", and alignItems: "center". Hopefully this solves the issue.

React-native view auto width by text inside

As far as I know, react-native stylesheet doesn't supports min-width/max-width property.
I have a view and text inside. The view in auto width doesn't resize by inherit text element.
How to fix that issue and make view width automatically set using text width?
My code is:
<View style={{backgroundColor: '#000000'}}>
<Text style={{color: '#ffffff'}}>Sample text here</Text>
</View>
In common HTML/CSS I would realize so:
<div style="background-color: #000; display: inline;">Sample text here</div>
Notice: flex: 1 on parent view is not helpful for me.
Text appears as
"Sam"
"ple"
"Tex"
"t"
"alignSelf" does the same as 'display: inline-block' would in common HTML/CSS and it works very well for me.
<View style={{backgroundColor: '#000000', alignSelf: 'flex-start' }}>
<Text style={{color: '#ffffff'}}>
Sample text here
</Text>
</View>
Two Solutions
Text component fits the text content
https://facebook.github.io/react-native/docs/text.html#containers
You can wrap <Text> components into another <Text> component.
By doing this everything inside is no longer using the flexbox layout but using text layout.
<Text>
<Text>{'Your text here'}</Text>
</Text>
View container adapt to nested Text component's content
In that case you need to use the props alignSelf in order to see the container shrink to the size of its content.
<View>
<View style={{ alignSelf: 'center', padding: 12}} >
<Text>{'Your text here'}</Text>
</View>
</View>
tldr: set alignItems to any value other than 'stretch' for the style of the parent View of your View containing Text
The issue your facing is likely related to React Native's default value for alignItems: 'stretch' on a <View /> element. Basically, all <View /> elements by default cause their children to stretch along the cross axis (axis opposite to the flexDirection). Setting alignItems to any value other than "stretch" ("baseline", "flex-start", "flex-end", or "center") on the parent View prevents this behavior and addresses your problem.
Below is an example where there are two View elements contained inside of a parent View with a blue border. The two View elements each contain a View wrapped around a Text element. In the case of the first View with default styling, the yellow child View expands horizontally to fill the entire width. In the second View where alignItems: 'baseline', the pink View does not expand and stays the size of it's child Text element.
<View style={{ borderWidth: 5, borderColor: 'blue' }}>
<View>
<View style={{ backgroundColor: 'yellow' }}>
<Text style={{ fontSize: 30 }}>Hello</Text>
</View>
</View>
<View style={{ alignItems: 'baseline' }}>
<View style={{ backgroundColor: 'pink' }}>
<Text style={{ fontSize: 30 }}>Hello</Text>
</View>
</View>
</View>
The align-items property is explained well here.
If you want to apply width to View based on <Text> , you can do something like this :
<View style={styles.container}>
<Text>
{text}
</Text>
</View>
const styles = StyleSheet.create({
container: {
flexDirection:"row",
alignSelf:"flex-start",
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
WORKING DEMO
This work for me.
<View style={{alignSelf: 'flex-start', backgroundColor: 'red'}}>
<Text>abc</Text>
</View>
Or simply:
<Text style={{color: '#ffffff', alignSelf: 'center'}}>Sample text here</Text>
Try this way for your requirement:
Here my height is Constant and i am enlarging the width by the length of text.
<View style={{flexDirection: 'row'}}>
<View style={{
height: 30, width: 'auto', justifyContent:'center',
alignItems: 'center'}}>
<Text style={{color:'white'}}>Now View will enlarge byitself</Text>
</View>
</View>
For Both Height and Width Try Changing the height inside the View style to 'auto' full code bellow:
<View style={{flexDirection: 'row'}}>
<View style={{
height: 'auto', width: 'auto', justifyContent:'center',
alignItems: 'center'}}>
<Text style={{color:'white'}}>Now View will enlarge byitself</Text>
</View>
</View>
Also try Giving padding to the View for arrange the text properly.
It works with Nicholas answer unless you want to center the view somewhere. In that case, you could add a wrapper view around the view to obtain the width of its content and set alignItems: 'center'. Like this:
<View style={{ flex: 1, alignItems: 'center' }}>
<View style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: 'red' }}>
<Text>{'Your text is here'}</Text>
</View>
</View>
The background color is added to show the size of the inner view
Just add alignSelf to the text style, it won't take the whole width
<View style={{flex: 1}}>
<Text style={{alignSelf: 'center'}}>{'Your text here'}</Text>
<Text style={{alignSelf: 'baseline'}}>{'Your text here'}</Text>
<Text style={{alignSelf: 'flex-end'}}>{'Your text here'}</Text>
<Text style={{alignSelf: 'flex-start'}}>{'Your text here'}</Text>
</View>
These worked for me
<View style={styles.imageCancel}>
<TouchableOpacity onPress={() => { this.setModalVisible(!this.state.visible) }}>
<Text style={styles.textCancel} >Close</Text>
</TouchableOpacity>
</View>
const styles = StyleSheet.create({
imageCancel: {
height: 'auto',
width: 'auto',
justifyContent:'center',
backgroundColor: '#000000',
alignItems: 'flex-end',
},
textCancel: {
paddingTop:25,
paddingRight:25,
fontSize : 18,
color : "#ffffff",
height: 50,
},
}};

Categories

Resources