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>
),
}}
/>
Related
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>
I have a TextInput that when pressed gets covered by the keyboard. So I wrapped it in a KeyboardAvoidingView. But regardless of the behavior that I set for this view, the TextInput won't move above the keyboard. Using position as the behavior moves the TextInput but only half way above the keyboard, while the other two don't seem to work at all.
I also tried wrapping my entire component with a KeyboardAvoidingView, but doing so breaks the entire layout.
Can anyone help me? I never managed to get KeyboardAvoidingView to work for me and now I really need it. Thanks in advance!
Here is my component. Also worth mentioning is that this component is top level(well, almost top level since it's wrapped in a Router)
const { height, width } = Dimensions.get('screen')
const style = StyleSheet.create({
main: {
height,
width,
flexDirection: 'column',
},
iconSelecter: {
width,
height: 196,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.primary,
marginTop: 32
},
icon: {
height: 164,
width: 164,
},
saveButton: {
width: 96,
height: 96,
borderRadius: 100,
backgroundColor: Colors.secondary,
alignItems: "center",
justifyContent: "center",
alignSelf: 'center',
position: 'absolute',
bottom: 96 + 32
},
saveIcon: {
height: 54,
width: 54,
},
textInputWrapper: {
borderBottomColor: Colors.textInputBorder,
width: 288,
borderBottomWidth: 1,
alignSelf: 'center',
marginTop: 96,
height: 48,
},
textInput: {
fontWeight: "300",
fontSize: 14,
margin: 0
},
hintWrapper: {
alignSelf: 'center',
marginTop: 4
},
hint: {
fontSize: 12,
fontFamily: "Roboto-Thin",
fontStyle: 'normal',
}
})
const CreateActivity = ({ goBack }: NavigationProps) => {
//////////////////////////////
//State and logic
///////////////
return (
// TODO: Add touchable opacity to dismiss keyboard
<View style={style.main}>
<Appbar title="New activity" canGoBack goBack={goBack} />
<View style={{ flex: 1 }}>
<View style={style.iconSelecter}>
<GestureRecognizer onSwipeLeft={nextIcon} onSwipeRight={lastIcon}>
<Image style={style.icon} source={icons[currentIconIndex]?.file}></Image>
</GestureRecognizer>
</View>
<View style={style.hintWrapper}>
<Text style={style.hint}>Swipe to cycle through the icons</Text>
</View>
<KeyboardAvoidingView>
<View style={style.textInputWrapper}>
<TextInput style={style.textInput} placeholder={"Give this activity a name"} value={name} onChangeText={setName}></TextInput>
</View>
</KeyboardAvoidingView>
<TouchableNativeFeedback onPress={createActivity} background={TouchableNativeFeedback.Ripple("#fff", true)}>
<View style={style.saveButton}>
<Image style={style.saveIcon} source={require("../../assets/icons/light/save.png")}></Image>
</View>
</TouchableNativeFeedback>
</View>
</View>
)
}
export default CreateActivity;
I suggest that you to try wrap all the content of the screen in <KeyboardAvoidingView /> (or make it one of the outermost elements), otherwise it only will slide up its children (the View and the TextInput) leaving the rest of the content in its original position, making the layout look overlaped and weird. If you do that, the value "position" should work fine.
Something like this:
<View style={style.main}>
<Appbar title="New activity" canGoBack goBack={goBack} />
<KeyboardAvoidingView behavior="position" >
<View style={{ flex: 1 }}> // --> Remove flex: 1 if you experience some issue with the positioning
<View style={style.iconSelecter}>
<GestureRecognizer onSwipeLeft={nextIcon} onSwipeRight={lastIcon}>
<Image style={style.icon} source={icons[currentIconIndex]?.file}></Image>
</GestureRecognizer>
</View>
<View style={style.hintWrapper}>
<Text style={style.hint}>Swipe to cycle through the icons</Text>
</View>
<KeyboardAvoidingView>
<View style={style.textInputWrapper}>
<TextInput style={style.textInput} placeholder={"Give this activity a name"} value={name} onChangeText={setName}></TextInput>
</View>
</KeyboardAvoidingView>
<TouchableNativeFeedback onPress={createActivity} background={TouchableNativeFeedback.Ripple("#fff", true)}>
<View style={style.saveButton}>
<Image style={style.saveIcon} source={require("../../assets/icons/light/save.png")}></Image>
</View>
</TouchableNativeFeedback>
</View>
</KeyboardAvoidingView>
</View>
Also see the comment in the code above. Check if you really need to use of flex: 1 in all the outer wrapper elements, and take a look to the height you are setting in the style.main based on dimentions. I don't think that it is necesary and I think it could lead to some measure issues if you fix the height of the parent container.
EDIT:
I was just digging in react-native docs and I realize that there is a zIndex that you could use to avoid ablsolute positioning. It is a relative style prop so it needs to be set between sibling views, like this:
export default class MyComponent extends React.Component {
render() {
return (
<View>
<View style={[styles.appbarShape, styles.appbarZIndex]} ><Text>Header</Text></View>
<KeyboardAvoidingView behavior="position" style={styles.contentZIndex}>
{other children}
<TextInput placeholder="enter text"/>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
appbarShape: {
height: 80,
width: Dimensions.get('window').width,
justifyContent: 'center',
alignSelf: "stretch",
backgroundColor: "#FFF"
},
appbarZIndex: {
zIndex: 3,
},
contentZIndex: {
zIndex: 0
}
});
Since the view that represents the appbar has a greater zIndex it shows up over the ones with a lower zIndex
Check this out working in this snack https://snack.expo.io/5VXAcw4Y0
Docs: https://reactnative.dev/docs/layout-props
Hope it helps!
Use react-native-keyboard-aware-scroll-view
<KeyboardAwareScrollView extraHeight={135} enabledOnAndroid={true}
extraScrollHeight={70} style={styles.mainContainer}
automaticallyAdjustContentInsets={true}
enableOnAndroid={true}
keyboardShouldPersistTaps='handled'
scrollEnabled={true} >
//your form
</KeyboardAwareScrollView>
const styles = StyleSheet.create({
mainContainer: { flex: 1, marginHorizontal: 15, marginVertical: 15 },
});
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
There is no transform-origin property in react-native so how can I do it ?
I think I should use transformX & transformY props.
const Marker = () => {
const rotate = new Animated.Value(???);
const transformX = new Animated.Value(???);
const transformY = new Animated.Value(???);
const doRotation = (newDegValue) => {
// ?????
}
return (
<View style={{ width: 200, height: 200, justifyContent: 'center', alignItems: 'center' }}>
<Animated.View transform={[{ rotate }, { transformX }, { transformY }]}>
<ArrowIcon width={30} height={30}>
</Animated.View>
{/* I need to rotate my ArrowIcon around this BaseIcon */}
<BaseIcon width={100} height={100} />
<View/>
)
}
Explanation
In this example, I am using the Animated Library. First, we are defining a Animated Value in the constructor. Then we are creating a triggerAnimation function, where we will animate this new value over time using the timing function.
In the render function, we are defining a new style object called animatedStyle. This style object will handle the rotation of the arrow. We are using the interpolate function to incrementally rotate the arrow. As inputRange we are allowing -1 to +1. -1 means a rotation of -360° and +1 a rotation of 360°-, as you can see in the outputRange.The interpolate function will automatically handle the mapping between input and output range.
In the return statement we are passing the animatedStyle to our <Animated.View>.
Now we can call the triggerAnimation function. As parameter we have to pass the desired rotation value.
Some Examples:
this.triggerAnimation(0.5) would result in a rotation of +180°.
this.triggerAnimation(-0.5) would result in a rotation of -180°.
this.triggerAnimation(0.25) would result in a rotation of +90°.
this.triggerAnimation(0.75) would result in a rotation of +270°.
Code
Constructor and triggerAnimation function:
constructor(props){
super(props);
this.state={
currentRotation: 0,
value: new Animated.Value(0),
}
}
triggerAnimation(newValue){
Animated.timing(this.state.value, {
toValue: newValue,
duration: 500,
}).start(() => {
// set rotated again
this.setState({currentRotation: newValue})
});
}
render function:
render() {
const animatedStyle={
transform: [
{
rotateZ: this.state.value.interpolate({
inputRange: [-1,1],
outputRange: ['-360deg', `360deg`],
}),
},
],
};
return (
<View style={styles.container}>
<View style={{justifyContent: 'center', flexDirection: 'column', alignItems: 'center', width: 150, height: 150}}>
<Animated.View style={[{justifyContent: 'center', flexDirection: 'row', alignItems: 'flex-start'}, StyleSheet.absoluteFill,animatedStyle]} >
<Icon name={"location-arrow"} size={30} style={{transform: [ {rotateZ: '-45deg' }]}}/>
</Animated.View>
<View style={{width: 70, height: 70, borderRadius: 35, backgroundColor: 'blue'}}/>
</View>
<TouchableOpacity onPress={()=>this.triggerAnimation(0.5)} >
<Text> 180° Trigger Animation </Text>
</TouchableOpacity>
...
</View>
);
}
Working Demo
https://snack.expo.io/B1UzO79Cr
You can use
transform: [{ rotate: '40deg' }] in styles
Example: => https://snack.expo.io/#msbot01/mad-cookie
export default class App extends React.Component {
constructor(props){
super(props)
this.state=({
angle:0
})
}
rotate(){
this.setState({
angle: this.state.angle + 5
})
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>{this.rotate()}} style={{position: "absolute", top:20, width:'100%'}}>
<Text style={{marginTop:20, position:'absolute', top:20}}>Click on me to rotate</Text>
</TouchableOpacity >
<Image style={{height:40, width:40}} source={require('./bus.png')} />
<View onPress={()=>{this.rotate()}} style={{position: "absolute"}}>
<Image style={{height:150, width:150, transform: [{ rotate: (this.state.angle+'deg') }]}} source={require('./ss.png')} />
</View >
</View>
);
}
}
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.