I've been trying to make a growing textinput with multiple lines now for a while, and is now finding my app in a situation where the keyboard blocks the text when entering a new line on the textinput. I've tried solutions ranging from KeyboardAwareScrollView-based solutions to ScrollView solutions, but alas nothing I've tried yet have worked.
I would really appreciate if someone could provide me with a solution that works for both android and iOS.
What I have now is a view with a textinput with multiline prop
<View style={{ height: this.state.height, flex: 1 }}>
<TextInput
{...otherProps}
placeholder="Skriv här"
placeholderTextColor="rgba(0,0,0,0.3)"
blurOnSubmit={false}
multiline
onContentSizeChange={this._onContentSizeChange}
style={[styles.textInputStyle, { height: this.state.height }]}
/>
</View>
with the _onContentSizeChange function as follows:
_onContentSizeChange = (event): void => {
const height = event.nativeEvent.contentSize.height;
this.setState({ height });
}
Problem illustrated with pictures:
imgur album
KeyboardAvoidingView seems to be the solution for you. You can set the distance between your view and the keyboard and calculate it with the height of your input. In the docs you find all properties: https://facebook.github.io/react-native/docs/keyboardavoidingview.html
You can also try using KeyboardAvoidingScrollView, then when your input is high enough you just scroll down the screen.
Here's an awesome article with great examples of keyboard avoids: https://medium.freecodecamp.org/how-to-make-your-react-native-app-respond-gracefully-when-the-keyboard-pops-up-7442c1535580
Hope it helps.
Related
I have a FlatList in React Native with fullscreen image and video items (with pagingEnabled). I want to have a short descriptive touchable text floating on top of the FlatList, to open up a view with some information about the image/video when pressed.
To make the user experience nice, I'd like to be able to scroll the FlatList through the touchable text as well. I.e. if the user happen to start their scrolling motion on top of the text, the FlatList would scroll but if it is a simple press event I'd like to open the view with details about the image/video.
No mater what I try I end up with either the text being able to react to the press OR the FlatList being able to scroll. I have tried different configurations with a custom PanResponder and pointerEvents but seem to always end up with a state were one thing does not work. Do you guys have any smart ideas? I am probably just stuck in the wrong train of thought.
This is a simplified view of my component structure:
<View>
<View style={{ position: 'absolute', bottom: 100, zIndex: 10 }}>
<TouchableOpacity onPress={() => console.log('press')}>
<Text>Some Descriptive Text</Text>
</TouchableOpacity>
</View>
<FlatList pagingEnabled horizontal {...otherFlatListProps} />
</View>
I'm having an issue with the React Native Picker.
My problem is that the selectedValue in the Picker does not show on small screens! On big screens it works just fine, but at a certain width, the selectedValue is not rendered, only the little downwards arrow.
When I manually set the width of the picker to be 100, it works fine, however, if the width is set to 50 (which is what I need), the selectedValue doesn't render, only the arrow downwards.
My code looks like this:
<Picker
selectedValue={props.selectedCurrency}
style={headerStyle.picker}
onValueChange={props.setCurrency}
>
<Picker.Item label="USD" value={Currency.USD} />
<Picker.Item label="EUR" value={Currency.EUR} />
</Picker>
And then the styles look like this:
picker: {
flex: 1,
width: 50,
},
I have figured out one solution, which is to render a component besides the Picker, but then the Text isn't clickable, which isn't the best UX design..
Can anyone help? I just want the selectedValue to render on both small and big screens.
Thank you very much in advance.
Unfortunately, if you need a minimum width to display correctly your items you do not have much choices. You can define a style for your items and maybe lower their size or use a font which render them lower. For example :
<Picker
selectedValue={props.selectedCurrency}
style={headerStyle.picker}
itemStyle={headerStyle.pickerItem}
onValueChange={props.setCurrency}
>
<Picker.Item label="USD" value={Currency.USD} />
<Picker.Item label="EUR" value={Currency.EUR} />
and for style
picker: {
flex: 1,
width: 50,
},
pickerItem: {
fontSize: 12
}
Or/and maybe you will have to change your UI in order to display the picker elsewhere in your screen to be sure to have enough space to display it correctly.
Hope this helps !
from this answer, you need to convert selected value to string.
<Picker
//...
selectedValue={props.selectedCurrency ? props.selectedCurrency.toString() : null}
//...
>
I have a render method with these components:
<View style={styles.root}>
{p.parts.map((part, index) =>
<Animated.View
key={`part${index}`}
style={[
this.draggables[index].pan.getLayout()
]}
{...this.draggables[index].panResponder.panHandlers}
>
<SphereView part={part} />
</Animated.View>
)}
</View>
And this style:
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
}
});
Drag and drop is working, but the problem is that when I drag the second element, it is shown above only the first and below all others. I want the component being dragged to be above all others.
I've tried various combinations with zIndex and none worked.
How to do it?
Edit: I've now just achieved with with the "elevation" style in Android. Is it the correct approach (even for iOS)?
It's a shame that most of the PanResponder questions on SO (or at least the ones Ive seen) have no answer. You can use zIndex. Just set the zIndex to 999 or something during the drag, and then on release, set it to the original zIndex+1, so that it STAYS above the other images. If you kept all of the dragged elements at 999, they would no longer overlap after one drag.
Elevation only works on android, but zIndex works on both, and will not create a shadow/3D effect
I have TextInput at the bottom of the screen. When TextInput is focused then keyboard appears and due to this all the flex view gets shrink to the available screen. I want to achieve that page layout should not change and input should be visible to user.
<View style={MainView}>
<View style={subMain1}>
<View style={{flex:1,backgroundColor:'#add264'}}></View>
<View style={{flex:1,backgroundColor:'#b7d778'}}></View>
<View style={{flex:1,backgroundColor:'#c2dd8b'}}></View>
</View>
<View style={subMain2}>
<View style={{flex:1,backgroundColor:'#cce39f'}}></View>
<View style={{flex:1,backgroundColor:'#d6e9b3'}}></View>
<View style={{flex:1,backgroundColor:'#69ee9a'}}>
<TextInput placeholder="input field"/>
</View>
</View>
</View>
const Styles = {
MainView:{
flex:1,
backgroundColor:'green'
},
subMain1:{
flex:1,
backgroundColor:'blue'
},
subMain2:{
flex:1,
backgroundColor:'orange'
}
}
I just found an answer to this.
I just have to use fixed height for my main container and inside that I can use flex layout. And If keyboard is hiding the content then we can use Scrollview that will allow scrollable interface when user clicks on input.
This helped me hope it can help others. :)
I'm not quite sure I understand what the issue you're running into, but I recently struggled with keyboard avoiding views in my app - for some reason, the native component just wasn't working.
Check this package out - it did the trick for me, and offers some cool customization capabilities:
https://github.com/APSL/react-native-keyboard-aware-scroll-view
Use 'react-native-keyboard-aware-scroll-view' and keep this as parent view and put all view inside that view. This node module creates scrollview and it listens to keyboard show event and automatically scrolls your screen so user can see the input box without getting it hidden behind the soft keyboard.
I am using React Native on a real Android device.
When creating a really simple app with just the following render function on the main app component...
render() {
<Image
source={{uri:'http://resizing.flixster.com/DeLpPTAwX3O2LszOpeaMHjbzuAw=/53x77/dkpu1ddg7pbsk.cloudfront.net/movie/11/16/47/11164719_ori.jpg'}}
style={
{
flex: 1,
resizeMode: 'contain',
backgroundColor: 'yellow'
}
} />
}
I get the following result on my device:
As you can see the whole background is yellow so that tells us the image element is taking the whole screen size indeed. But it is just rendered wrong.
The 'cover' resizeMode does work as expected (and so does the 'stretch' mode).
It is the 'contain' mode that is not working (the most important one from my point of view).
The problem gets even worse when placing the image on a ListView since the image does not even show.
UPDATE 1
As Frederick points out, 'contain' only works when the image is larger than the container size. So how can we make the image take the whole container size while keeping its aspect ratio?
Percentages are not supported yet by styles in React, and I don't know how to get the image width and height properties once the image is loaded. None of the events associated with the Image component provide that info.
UPDATE 2
Good news. I am now using React Native v0.24.1 and it seems the image 'contain' mode now works as expected, even when the actual image size is smaller than its container.
zvona's solution is good (although you need to bear in mind that onLayout will give you the image view size the image is rendered in, but NOT the actual image size being loaded). As for now, I don't know of any way to find out the actual image size (let's suppose you are retrieving the image from a network resource and you don't know the size, which could be very important if you want to calculate its aspect ratio).
This is the trick:
render() {
return (
<Image
style={{ flex: 1, height: undefined, width: undefined }}
source={require("../../resource/image/bg_splash.jpg")}
resizeMode="contain"
/>
);
}
This is the latest solution:
Image.resizeMode.contain is not working with latest version of react native so i use it like this:
import ImageResizeMode from 'react-native/Libraries/Image/ImageResizeMode'
<Image source={image} resizeMode={ImageResizeMode.contain} />
This is what worked for me with the latest react-native 0.37:
<Image source={require('../images/my-image.png')} resizeMode={Image.resizeMode.center} />
Answering the updated part of the question. You can get the image size for external images using Image.getSize.
For local images, a not so documented way to figure out the size and thereby calculate the aspect ratio is using resolveAssetSource which is a react-native module (no need for an external library):
let resolveAssetSource = require('resolveAssetSource')
let { width, height } = resolveAssetSource(image_source)
let aspectRatio = width / height
My answer to UPDATED part of the question:
<Image source={{uri:'...'}} onLayout={this.onImageLayout} />
where:
onImageLayout: function(data){
console.log('layout', data.nativeEvent.layout);
}
These should be proportioned to device width + height, which you get with:
const {
Dimensions,
.
.
.
} = React;
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
And if you want to get width/height as percentages in styles, you just define e.g.:
const styles = StyleSheet.create({
image: {
width: windowWidth * 0.75,
height: windowHeight * 0.33
}
});
"Contain" only resizes your image when the image size is larger than the container you're trying to fit it in. In this case, your container is the full screen. The image you're loading via the URL is way smaller, since it's only 53 by 77 pixels. So it won't resize.
I think "cover" should do what you're trying to achieve. However, due to the size the image, it won't look very nice when it is magnified like that.
Made an example here: https://rnplay.org/apps/X5eMEw
Adding the aspect ratio worked for me:
<Image
source={item.imageUrl && item.imageUrl != '' ? { uri: item.imageUrl } : require('../../assets/images/no-image-icon-15.png')}
style={{
flex: 1,
aspectRatio: 1.5,
height: undefined,
width: undefined
}}
resizeMode="contain"
/>
I will suggest you a simple solution to apply if it's convenient for you.
use "ImageBackground" component of react-native.
so your code will become something like this:
<ImageBackground
source={{uri:'http://resizing.flixster.com/DeLpPTAwX3O2LszOpeaMHjbzuAw=/53x77/dkpu1ddg7pbsk.cloudfront.net/movie/11/16/47/11164719_ori.jpg'}}
style={{flex: 1}} />
In your particular case you are apparently trying to cover whole screen with the image so this solution is exactly for that purpose. However most of the time it works in other cases as well where you want to fit the image properly in its view.