How to scroll text input in React Native as content get bigger? - javascript

I currently have an auto-growing text input field that will expand to a certain clamped height.
The issue is that once the text input has grown to its max size, the content that I write gets pushed under the container. See attached images
Before Issue:
Expanded view, before issue
Post Issue:
Cursor has disappeared from view
I though about having the Text Input scroll to the end, but I haven't found any documentation or API that allows me to scrollTo end of the input. (Similar to ScrollView)
The final functionality is that of a chat application messaging box.
Code:
import React, { Component } from 'react';
import { View, TextInput } from 'react-native';
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
class AutogrowTextInput extends Component {
constructor(props) {
super(props);
this.state = {'contentHeight': props.minHeight};
this.resize = this.resize.bind(this);
}
render() {
let height = clamp(this.state.contentHeight, this.props.minHeight, this.props.maxHeight) + 20;
let inputStyle = {height: height, flex: 0};
return (
<TextInput
{...this.props}
style={[this.props.style, inputStyle]}
onContentSizeChange={this.resize}
underlineColorAndroid="transparent"
/>
);
}
resize(event) {
this.setState({
'contentHeight': event.nativeEvent.contentSize.height,
});
}
}
export default AutogrowTextInput;
Instantiation of Element.
return (
<View style={style.footer}>
<TouchableIcon
onPress={this._attach}
icon="add"
color="#000"
/>
<AutogrowTextInput
underlineColorAndroid="transparent"
style={style.input}
placeholder="Send Message"
value={message}
onChangeText={this._changeText}
autoFocus={false}
multiline={true}
minHeight={30}
maxHeight={100}
/>
{attachmentComps}
<TouchableIcon
disabled={sending || isEmpty}
onPress={this._sendMessage}
icon="send"
color="#000"
/>
</View>
);
Styling:
footer: {
borderTopWidth: StyleSheet.hairlineWidth,
borderColor: '#ccc',
paddingVertical: 4,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 10,
backgroundColor: '#fff',
flex: 0
},
input: {flexGrow: 1, fontSize: 16, paddingHorizontal: 10, paddingVertical: 1},
attachmentContainer: {marginHorizontal: 4},
attachment: {height: 40, width: 40, borderRadius: 2},
Thanks in advance.
P.S. Cannot post images in line because of lack of reputation.

Related

React Native Paper ProgressBar Not visible

Hello i was following along a tutorial and it was going fine, until the tutor used react-native-paper ProgressBar in his project, I wrote exactly same but it was not visible, I found the documentation and plugged it, still not visible.
What am i Doing wrong?
import {View, StyleSheet, Text} from 'react-native' ;
import { ProgressBar, Colors } from 'react-native-paper';
import CountDown from '../comps/CountDown.js' ;
const Timer = ({task}) => {
return (
<View style={styles.container}>
<CountDown />
<Text style={styles.text}> Focusing On:</Text>
<Text style={[styles.text, styles.textBold]}> {task} </Text>
<ProgressBar progress={0.4} color='#5E84E2' style={styles.progress}/>
</View>
) ;
}
const styles = StyleSheet.create({
container : {
flex: 1,
width: 100,
alignItems: 'center' ,
justifyContent: 'center',
paddingTop: 40,
},
text: {
// flex: 0.5,
color: 'white'
},
textBold: {
fontWeight: 'bold' ,
// borderColor: 'white',
// borderWidth: 1,
// borderStyle: 'solid' ,
},
progress: {
height: 10,
// borderColor: 'white',
// borderWidth: 1,
// borderStyle: 'solid' ,
}
}) ;
export default Timer ;
Also, If i were to Give border to the progressBar, it appears but keep on increasing in width even when width is more than the viewport width. I dont know what is happening
The problem here is when you use alignItems the children components need to have a fixed width, your progress bar doesnet have a fixed width.
You will have to provide a with in the styles.
progress: {
height: 10,
width:50
}
Based on documentation default value for width is
auto (default value) React Native calculates the width/height for the
element based on its content, whether that is other children, text, or
an image.
Better have a value for width which will solve your issue.
A responsive solution
Currently ProgressBar doesn't support the flex styling system.
If someone is also looking to make the width equal to 100% if it's parrent component, there is a good recommended solution provided here.
Just set the width to undefined:
progress: {
height: 10,
width:undefined
}
e.g.
<View style={{ flex: 2, other flex styles }}>
<ProgressBar progress={0.5} color="white" style={{ height: 5, width: undefined }} />
</View>

Use percentage/relative values in Styling of <View /> on iOS in react-native

I want to do something like this in react-native
<View
style = { width: '100%', padding: 10, borderRadius: '25%' }>
{...}
</View>
But I get (on iOS)
JSON value '25%' of type NSString cannot be converted to NSNumber
Is there a way to use relative/percentage values as styling input for borderRadius?
You can do something like that, use ref in order to have a reference to that element, then you can measure its width (not in percentage) and you can calculate the border radius.
import React, { useRef, useEffect, useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
export default function App() {
const ref = useRef();
const [width, setWidth] = useState(0);
useEffect(() => {
ref.current.measure((x, y, w, h) => {
setWidth(w);
});
}, []);
return (
<View style={styles.container}>
<View
ref={ref}
style={{
width: '80%',
padding: 10,
backgroundColor: 'red',
borderRadius: width / 4, // 25%
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
Okay, after a quick researching, i found that borderRadius style expects number as a value, not a String
So if you want to custom the value of borderRadius, you can do sth like this:
<View style={{
width: 300,
height: 300,
backgroundColor: 'red',
padding: 10,
borderRadius: 300/4 // equal 25% of its size
}}>

REACT NATIVE: Scroll View keep bouncing back up to the top doesn't scroll

I am trying to get my ScrollView to scroll down so I can see all my views but it keeps bouncing back up to the top.
What it looks like
The White panels below Physiological Classification are in the ScrollView and are supposed to scroll but it just bounces back to the top.
Here is where my ScrollView is
_renderContent = (section) => {
section.arrow = !(section.arrow);
return (
<ScrollView>
<QuestionPanels
Questions={section.Questions}
pickerDefaultValues={section.pickerDefaultValues}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
);
};
Question Panels is just a component that generates Views based on a json file. If relevant here is the code for that as well.
import React, { Component, useState, setState} from 'react';
import { ScrollView, View, Text } from 'react-native'
import styles from '../../Styles/GeneralStyles';
import Picker from '../Picker/Picker'
import appData from '../../DataSheet/appData.json';
const Panel = (props) => {
return (
<View style={styles.dropDownCardPanel}>
<View style={styles.QuestionPanel}>
<Text style={styles.QuestionText}>{props.Question}</Text>
</View>
<View style={styles.AnswerPanel}>
<Picker
defaultVal= {props.pickerDefaultValues}
showButton={false}
pickerItemNames={props.pickerItemNames}
/>
</View>
</View>
);
}
export default class QuestionairePanels extends Component {
constructor(props){
super(props)
}
render() {
var panelList = [];
for(let i = 0; i < this.props.Questions.length; i++){
panelList.push(
<Panel
key={i}
Question={this.props.Questions[i]}
pickerDefaultValues ={this.props.pickerDefaultValues[i]}
pickerItemNames = {this.props.pickerItemNames[i]}
/>
);
}
return(
<View>
{panelList}
</View>
);
}
}
Here are the styles that matter for this
dropDownCardPanel: {
height: 200,
flexDirection: "row",
backgroundColor: '#faf2f2',
paddingTop: 5,
paddingBottom: 5,
borderBottomWidth: 1,
borderColor: 'black',
},
dropDownCardPanelText: {
fontSize: 15,
color: "black",
marginLeft: 10
},
icon: {
left: 20,
},
QuestionPanel: {
height: "100%",
width: "60%",
borderRightWidth: 1,
borderColor: 'black',
left: 3,
marginRight: 5,
alignItems: "center",
justifyContent:'center'
},
QuestionText: {
fontSize: 20,
},
AnswerPanel: {
height: "100%",
width: "40%",
right: 3
}
The reason why I was not able to scroll was that the tag needs a specific height for it to render for a scroll. So to fix this I had to put a view around the and set it to a specific height.
Corrected Code
section.arrow = !(section.arrow);
var scrollHeight = (section.Questions.length > 2) ? 600 : 400;
var headerDisplay = (section.name.match("Physiologic")) ? section.name + " Variables" : "Select Dominant Final Diagnosis";
return (
<View style={{height: scrollHeight}}>
<ScrollView>
<View style={styles.dropDownCardPanelHeader}>
<Text style={styles.dropDownCardPanelHeaderText}>
{headerDisplay}
</Text>
</View>
<QuestionPanels
Questions={section.Questions}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
</View>
Later I will try to use Flex to set the height so the react native project can scale to all screens but so far it works for iOS

React-native: Absolute positioned components not rendering completely unless a state is changed

I have a screen that has several components, some with relative positioning and some with absolute. When the screen is opened, the absolute positioned components don't go all the way to their final correct position, or size. They appear on the screen, but in the wrong spot and size. Only by changing a state with a button press, do the components finish rendering. Here's a simplified version of the screen below:
The screenshot on the left has the absolutely positioned menu icon (3 horizontal lines at the top right), not all the way in its correct position yet. The screen on the right shows the icon in its final correct position, only after I changed a state by pressing a touchable region.
Here is the code:
import React, {Component} from 'react';
import {View, Text, StyleSheet, Animated, Image, Dimensions, TouchableWithoutFeedback} from 'react-native';
import colors from '../config/colors';
import Icon from 'react-native-vector-icons/Ionicons';
import Orientation from 'react-native-orientation-locker';
const w = (Dimensions.get('window').width);
const h = (Dimensions.get('window').height);
const r = h / w;
if (r > 1.9) {
mission_font_heading = 14;
mission_font = 12;
check_dimension = 14;
name_font = 10;
} else {
mission_font_heading = 12;
mission_font = 10;
check_dimension = 12;
name_font = 8;
}
class how_to_play9_screen extends Component {
constructor (props) {
super(props);
this.state = {
test: 0, //changing this state "fixes" the component's position
}
}
componentDidMount() {
Orientation.lockToPortrait();
}
openMenu() {}
showMission() {
this.setState({test: 2}); //this changes the state
}
render() {
return (
<View>
<Image
style={styles.backgroundImage}
source={require('../images/Background.png')}
>
</Image>
<View style={{alignItems: 'center'}}>
<TouchableWithoutFeedback onPress={() => this.showMission()}>
<View style={styles.missionContainer}>
<Text style={styles.missionTitleText}>
MISSION TITLE
</Text>
<Text style={styles.text}>
(X VP)
</Text>
<View style={{flexDirection: 'row'}}>
<Image
style={styles.checkBox}
source={require('../images/Checkbox.jpg')}
/>
<Text style={styles.missionReqText}>
Mission Requirement 1
</Text>
</View>
</View>
</TouchableWithoutFeedback>
</View>
// below is the absolute positioned component
<View
style={{
position: 'absolute',
top: 5,
right: 5,
}}
>
<TouchableWithoutFeedback
onPress={() => this.openMenu()}
>
<Icon
name='ios-menu'
size={(Dimensions.get('window').width) * .08}
color={colors.JBDarkTeal}
/>
</TouchableWithoutFeedback>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
backgroundImage: {
width: (Dimensions.get('window').height) * .65,
height: (Dimensions.get('window').height) * 1.3,
position: 'absolute',
left: 0,
top: 0,
},
missionContainer: {
backgroundColor: colors.JBTealTrans,
borderWidth: 2,
borderColor: colors.JBTan,
marginVertical: 10,
paddingVertical: 3,
paddingRight: 5,
width: '80%',
},
missionTitleText: {
fontSize: mission_font_heading,
textAlign: 'center',
color: colors.JBTan,
marginVertical: 3,
marginHorizontal: 5,
},
text: {
fontSize: mission_font,
textAlign: 'center',
color: colors.JBTan,
marginVertical: 3,
},
missionReqText: {
fontSize: 12,
color: colors.JBTan,
marginVertical: 3,
marginLeft: 5,
marginRight: 5,
},
checkBox: {
width: check_dimension,
height: check_dimension,
marginVertical: 3,
marginLeft: 5,
},
})
export default how_to_play9_screen;
I want the icon and any other absolute positioned components to go straight to their final position upon screen opening.
Edit: I should also mention that the previous screen locks the orientation to landscape, and this screen locks it back to portrait. I found that if I load this screen from a different screen that is already in portrait, then this one renders correctly, so it seems that it doesn't like me changing the orientation.
The position is not the problem, the problem is size of <Icon /> try to using number instad Dimensions calc, or make this calc on componentDidMount or render

Display value of slider with respect to slider thumb react native

I want to move the label with respect to slider thumb just like this one:
Right now my slider is like this:
I want to display the label shown as 30 km with respect to the slider thumb such that as the slider moves, the label should move accordingly.
I am using Native React Slider component.
this is my code:
<Slider
style={styles.slider}
thumbTintColor='rgb(252, 228, 149)'
step={1}
maximumValue={5}
thumbTintColor='rgb(252, 228, 149)'
maximumTrackTintColor='#494A48'
minimumTrackTintColor='rgb(252, 228, 149)' />
You can adjust left of the text to the value of the slider.
const left = this.state.value * (screenWidth-60)/100 - 15;
<Text style={ { width: 50, textAlign: 'center', left: left } }>
{Math.floor( this.state.value )}
</Text>
<Slider maximumValue={100}
value={this.state.value}
onValueChange={value => this.setState({ value })} />
Solution to your problem:
constructor(props){
super(props)
this.state = {
distance: 30,
minDistance: 10,
maxDistance: 100
}
}
render() {
return (
<View style={styles.container}>
<Slider
style={{ width: 300}}
step={1}
minimumValue={this.state.minDistance}
maximumValue={this.state.maxDistance}
value={this.state.distance}
onValueChange={val => this.setState({ distance: val })}
thumbTintColor='rgb(252, 228, 149)'
maximumTrackTintColor='#d3d3d3'
minimumTrackTintColor='rgb(252, 228, 149)'
/>
<View style={styles.textCon}>
<Text style={styles.colorGrey}>{this.state.minDistance} km</Text>
<Text style={styles.colorYellow}>
{this.state.distance + 'km'}
</Text>
<Text style={styles.colorGrey}>{this.state.maxDistance} km</Text>
</View>
</View>
);
}
}
Styles:
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#000',
},
textCon: {
width: 320,
flexDirection: 'row',
justifyContent: 'space-between'
},
colorGrey: {
color: '#d3d3d3'
},
colorYellow: {
color: 'rgb(252, 228, 149)'
}
});
Output:
Working Snippet:
https://snack.expo.io/Syrt3Bs7z
Built in <Slider /> doesn't provide those flexibility to customize what you want.
This should works, react-native-slider, which is a drop-in replacement of official <Slider />.
What you need is very similar to it's demo style #4.
For your Slider Label of value, you can modify its function _renderThumbImage() to replace default <Image />.
measure the size and position of the slider View
<Slider
maximumValue={10}
onLayout={(event)=>{this.slider_bound(event)}}/>
//majore size of Slider.
slider_bound=(event)=>{
var {x, y, width, height} = event.nativeEvent.layout;
this.state.slider_Width=width;
this.state.slider_Height=height;
this.state.slider_x = x;
this.state.slider_y = y;
this.state.slider_x_step_size = width/10; //Devide the width by slider maximum value
this.setState({triger:''});
console.log(TAG+"Slider Dimen:"+width,height+'pos:',x,y);
}
2.Now in "onValueChange" callback of slider.
//compute position to show slider value txt
this.state.value_x = (value * this.state.slider_x_step_size) + this.state.slider_x;
this.state.value_y = this.state.slider_Height + this.state.slider_y;
Show the slider Value txt on Calculated positions.
<Text style={{position:'absolute',top:this.state.value_y,left:this.state.value_x,color:colors.blackc}}>{this.state.data.slider_value}</Text>
..............
that will do the job, but You might have to tweak it little bit.
I think react-native-multi-slider will solve your problem . You can change the slider thumb by sending your custom designed component to the customMarker prop that is available. Then you can wrap the Multislider in another component, maintain the state(for slider position value) there and send value as prop to your custom designed marker everytime the thumb position changes which can be detected using onValuesChange prop.
This link might also help you.
import {Slider} from 'react-native-elements';
<Slider
thumbStyle={{height: 15, width: 15, backgroundColor: 'orange'}}
maximumTrackTintColor="grey"
minimumTrackTintColor="orange"
thumbProps={{
children: (
<View
style={{
color: 'green',
marginTop: -22,
width: 100,
}}>
<Text>Text</Text>
</View>
),
}}
/>

Categories