I am trying to have my Animated View come downward and over the content of the ScrollView instead of over my top fixed position bar, but when the Animation begins the animated view ends up eating the space of the container bar. I have played with paddingTop, marginTop, but seems to be a hack.
Here is a self contained code sample that shows what I'm trying to do:
import React, { Component } from 'react';
import {
AppRegistry, StyleSheet, Text, View, Animated, Dimensions, ScrollView,
Button
} from 'react-native';
const { width } = Dimensions.get('window');
const make_text = (text='Hello', color='blue') => (
<Text
style={{textAlign: 'center', fontSize: 24, backgroundColor: color, margin: 20}}>
{text}
</Text>
);
class Fix_bar extends Component {
state = { height: new Animated.Value(0) };
expand_dropdown = () => {
Animated.timing(this.state.height, {
toValue: 100
}).start();
}
fold_dropdown = () => {
Animated.timing(this.state.height, {
toValue: 0
}).start();
}
render () {
const s = {
position: 'absolute', height: 150, backgroundColor: 'red', paddingTop: 20, width
};
return (
<View style={s}>
<View style={{flexDirection: 'row', flex: 1, justifyContent: 'space-between'}}>
<Text style={{fontSize: 24, paddingTop: 50}}> Left side thing</Text>
<Text style={{fontSize: 24, paddingTop: 50}}> Right side thing</Text>
</View>
<Button title={'Expand'} onPress={this.expand_dropdown}/>
<Button title={'Fold'} onPress={this.fold_dropdown}/>
<View style={{backgroundColor: 'black', height: 1}}/>
<Animated.View style={{height: this.state.height}}>
{make_text('world', 'aliceblue')}
{make_text('world', 'aliceblue')}
{make_text('world', 'aliceblue')}
</Animated.View>
</View>
);
}
}
class animate_example extends Component {
render() {
return (
<View style={{backgroundColor: 'orange', flex: 1}}>
<Fix_bar/>
<ScrollView style={{marginTop: 150}}>
<View style={{justifyContent: 'space-between'}}>
{make_text()}
{make_text()}
{make_text()}
{make_text()}
{make_text()}
</View>
</ScrollView>
</View>
);
}
}
AppRegistry.registerComponent('animate_example', () => animate_example);
One idea I had was to make a trailing View in that fix_bar component with transparency with the height that I intend for the dropdown but haven't explored that idea.
I would suggest the following hierarchy:
const ScrollViewContainer = () =>
<ScrollView style={{marginTop: 150}}>
<View style={{justifyContent: 'space-between'}}>
{make_text()}
{make_text()}
{make_text()}
{make_text()}
{make_text()}
</View>
</ScrollView>;
const ExpandableBar = (props: {expanded: boolean}) =>
<View style={{position: "absolute", top: 0, left: 0, right: 0, bottom: 0}} />
const render = () =>
<View>
<Fix_bar />
<View style={{flex: 1}}> // container which fills remaining space
<ScrollViewContainer />
<ExpandableBar />
</View>
Then in ExpandableBar you'd animate down if expanded is true. also please note that ExpandableBar should be a class (obviously).
Related
Task at hand:
What I've taken a lot at so far:
Pie chart from [https://github.com/JesperLekland/react-native-svg-charts-examples][2]
Here I divided the entire pie into 50 units of 1 value, inorder to get the Split-bar effect. I can pass the color according to the image shown above.
But how can I add the inner lines(red and green) and the data inside?
Any help would be appreciated!
So the approach I would take is to make the outer ring a PieChart (like you have done), but to make the inner circle a ProgressCircle (https://github.com/JesperLekland/react-native-svg-charts#progresscircle) as this component naturally looks like the inner circle in the picture. You can change its backgroundColor prop to red and its progressColor prop to green.
import {PieChart, ProgressCircle} from 'react-native-svg-charts';
import Svg, {Text as SvgText, ForeignObject} from 'react-native-svg';
import Icon from 'react-native-vector-icons/FontAwesome5';
// ...
class CustomPieChart extends React.PureComponent {
render() {
const data = Array.apply(null, Array(50)).map(Number.prototype.valueOf, 1);
// Change to whatever your fill function looks like...
const getFill = (index) => {
if (index > 30) return 'purple';
if (index > 20) return 'blue';
if (index > 10) return 'green';
return 'red';
};
const pieData = data.map((value, index) => ({
value,
svg: {
fill: getFill(index),
},
key: `pie-${index}`,
}));
return (
<PieChart innerRadius="90%" style={styles.pieChart} data={pieData}>
<ProgressCircle
style={styles.progressCircle}
progress={0.7}
backgroundColor="red"
progressColor="green">
<ForeignObject x={-100} y={-100}>
<View style={styles.progressCircleContentContainer}>
<Text style={{...styles.text, color: 'green', marginBottom: 5}}>
Active
</Text>
<View
style={{
...styles.progressCircleContentView,
width: 110,
}}>
<Icon name="heartbeat" size={30} color="red" />
<Text style={styles.text}>72 BPM</Text>
</View>
<View style={styles.progressCircleContentView}>
<Icon name="shoe-prints" size={30} color="red" />
<Text style={styles.text}>4,565</Text>
<Icon name="bolt" size={30} color="red" />
<Text style={styles.text}>45 min</Text>
</View>
<View style={styles.progressCircleContentView}>
<Icon name="fire-alt" size={30} color="red" />
<Text style={styles.text}>1,856</Text>
<Icon name="glass-whiskey" size={30} color="red" />
<Text style={styles.text}>Active</Text>
</View>
<View style={{...styles.progressCircleContentView, width: 150}}>
<Icon name="moon" size={30} color="red" />
<Text style={styles.text}>6 hr 10 min</Text>
</View>
</View>
</ForeignObject>
</ProgressCircle>
</PieChart>
);
}
}
const styles = StyleSheet.create({
pieChart: {
height: 300,
},
progressCircle: {
height: 250,
marginTop: 25,
},
progressCircleContentContainer: {
alignItems: 'center',
width: 200,
height: 200,
transform: [],
},
progressCircleContentView: {
flexDirection: 'row',
justifyContent: 'space-between',
width: 200,
marginBottom: 5,
},
text: {
fontSize: 20,
},
});
What this exmample doesn't do:
Add the backdrop shadow on the circle
Add the icons on the outside of the outer circle
The custom shapes were made with react-native-svg, the library that is used by react-native-svg-charts internally. You can read its documentation here: https://github.com/react-native-community/react-native-svg.
How it looks:
Please have a look this image
I have an issue in the animation of the circle.
The flow is:
When user will click on button 1, the circle will animate from real position to position 1,
When clicking button2, a circle will move from position 1 to position 2,
and
When clicking on button2, a circle will animate back on the real position.
I need 1 sec. time while animate and I want to set circle position at particular Y position.
mean the first position on Y=400, second position on Y= 100.
Thanks in advance
You need to use the Animated library from react-native. Check out the library for more details on how to animate objects.
Meanwhile check the working example in Snack.io
Here's the code.
import React, { Component } from "react";
import { View, Text, StyleSheet, Animated, TouchableOpacity } from "react-native";
export default class App extends Component {
constructor() {
super();
this.state = {
posY: new Animated.Value(400)
};
}
moveBall = (yPos) => {
Animated.timing(this.state.posY, {
toValue: yPos,
duration: 1000
}).start()
};
renderRectangle = () => {
const animatedStyle = { top: this.state.posY };
return (
<Animated.View style={[styles.rectangle, animatedStyle]}>
</Animated.View>
);
};
render() {
return (
<View style={styles.container}>
<View style={{ flex: 0.9, alignItems: 'center' }}>
{this.renderRectangle()}
</View>
<View style={styles.buttonsContainer}>
<TouchableOpacity
style={styles.buttonStyle}
onPress={() => this.moveBall(250)}
>
<Text>Button 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonStyle}
onPress={() => this.moveBall(100)}
>
<Text>Button 2</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonStyle}
onPress={() => this.moveBall(400)}
>
<Text>Button 3</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
rectangle: {
backgroundColor: "#2c3e50",
width: 50,
height: 50,
borderRadius: 50,
position: 'absolute'
},
buttonsContainer: {
flex: 0.1,
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: 20,
paddingRight: 20
},
buttonStyle: {
padding: 5,
height: 30,
backgroundColor: 'limegreen'
}
});
I am trying to show a modal without going to another screen, I want it to be displayed on the current screen but not through navigation. The modal doesn't pop up and I don't know the problem.
I am using the renderModal to show the modal on the screen.When I use this.props.navigation.navigate('AnotherModal'), it works but goes to another screen,this time I want to show modal on the same screen.
import * as React from 'react';
import { Text, View, Image,
StyleSheet,Alert,Modal,TouchableHighlight } from 'react-native';
import { Constants } from 'expo';
import { Appbar, Colors, FAB } from 'react-native-paper';
import ProductsModal from './ProductsModal';
import ModalTester from './ModalTester';
export default class AppBar extends React.Component {
state = {
modalVisible: false,
};
setModalVisible(visible)
{
this.setState({modalVisible: visible});
}
renderModal() {
return (
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={{marginTop: 22}}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
);
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.appbar}>
<Appbar style={styles.piece}>
<Appbar.Action
icon={require('../../assets/devices.png')}
onPress={this.renderModal.bind(this)}
/>
</Appbar>
<View>
<Image
source={require('../../assets/cutout.png')}
style={styles.cutout}
pointerEvents="none"
/>
<FAB
icon={require('../../assets/add_circle.png')}
color="#b2beb5"
onPress={() => navigate('Homes')}
style={styles.fab} />
</View>
<Appbar style={[styles.piece, styles.right]}>
<Appbar.Action
icon={require('../../assets/account_box.png')}
onPress={() => console.log('Account pressed')}
/>
</Appbar>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
//backgroundColor: '#ecf0f1',
padding: 8,
},
appbar: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: 56,
flexDirection: 'row',
},
piece: {
flex: 1,
backgroundColor: Colors.grey300,
},
right: {
justifyContent: 'flex-end',
},
cutout: {
height: 56,
width: 80,
tintColor: Colors.grey300,
},
fab: {
position: 'absolute',
margin: 12,
bottom: 16
}
});
You should try to bind your setModalVisible in the constructor first:
constructor(props) {
super(props);
this. setModalVisible = this. setModalVisible.bind(this);
}
And then change your first Appbar.Action to something like this:
<Appbar.Action
icon={require('../../assets/devices.png')}
onPress={() => this. setModalVisible(true)}
/>
Also you have to add your Modal to the rendered code
...
<Appbar.Action
icon={require('../../assets/account_box.png')}
onPress={() => console.log('Account pressed')}
/>
</Appbar>
{this.renderModal()}
</View>
I'm not sure the bind is necessary though
Since screen does not change, your Modal needs to be within the render method of that screen. This means it can be handled by the state of the component. For example to show it you can:
<Appbar.Action
icon={require('../../assets/devices.png')}
onPress={() => this.setModalVisible(true)}
/>
In the main render you can just add directly your renderModal, 'cause its visible prop is sufficient to handle the behavior:
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.appbar}>
<Appbar style={styles.piece}>
<Appbar.Action
icon={require('../../assets/devices.png')}
onPress={this.renderModal.bind(this)}
/>
</Appbar>
<View>
{this.renderModal()}
<Image
source={require('../../assets/cutout.png')}
style={styles.cutout}
pointerEvents="none"
/>
<FAB
icon={require('../../assets/add_circle.png')}
color="#b2beb5"
onPress={() => navigate('Homes')}
style={styles.fab} />
</View>
<Appbar style={[styles.piece, styles.right]}>
<Appbar.Action
icon={require('../../assets/account_box.png')}
onPress={() => console.log('Account pressed')}
/>
</Appbar>
</View>
);
}
Anyone has experience of implementing react-native-modal?
While I'm using it, modal isn't closed when I tap outside of modal.
Here is what I've tried
Adding onBackdropPress(() => {this.props.hideModal()})
Adding TouchableWithoutFeedback inside and outside of components
and many other approaches...
Here is my the screen where I want to show my modal.
render() {
return (
<View style={{flex: 1}}>
<ScrollView>
// CONTENT HERE
{this._renderModal()} //rendering modal here
<FABs onFABsPress={this._showModal} /> // I open Modal when I press the FABs button
</ScrollView>
</View>
)
);
_renderModal = () => {
return (
<CameraImageSelectModal
hideModal={this._hideModal}
isModalVisible={this.state.isModalVisible}
navigation={this.props.navigation}
/>
)
}
Here is modal component : CameraImageSelectModal.js
render() {
let { isModalVisible } = this.props;
return (
<View>
<Modal
isVisible={isModalVisible}
onBackdropPress={() => {console.log('hey')}}>
transparent={true}>
<View style={styles.modalContainer}>
<View style={styles.modalTitleTextContainer}>
<Text style={styles.modalTitleText}>Hello World</Text>
</View>
<View style={styles.modalContentTextContainer}>
<Text style={styles.modalContentText}></Text>
</View>
<View style={styles.modalButtonContainer}>
<Button transparent onPress={this._handleCameraPress}>
<Text style={[styles.modalText, styles.black]}>Camera</Text>
</Button>
<Button transparent onPress={this._handleAlbumPress}>
<Text style={styles.modalText}>Album</Text>
</Button>
</View>
</View>
</Modal>
</View>
Thanks!!
I don't think the modal has that built in functionality, but you could create your own component that does. Here is a quick implementation. You might have to mess around with padding and margin to get it how you like, but this will allow the modal to be dismissed when pressing outside.
import React, { Component } from "react"
import { Modal, StyleSheet, View, TouchableHighlight } from "react-native"
const styles = StyleSheet.create({
container: {
zIndex: 1,
margin: 25,
backgroundColor: "white"
},
background: {
flex: 1
},
outerContainer: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: "center"
}
})
const MyModal = props => (
<Modal transparent={true} animationType={"slide"} visible={props.visible} onRequestClose={() => props.onRequestClose()}>
<TouchableHighlight style={styles.background} onPress={() => props.onRequestClose()} underlayColor={"transparent"}>
<View />
</TouchableHighlight>
<View style={ styles.outerContainer }>
<View style={styles.container}>
{props.children}
</View>
</View>
</Modal>
)
export { MyModal }
I just figured out why onBackdropPress = {() => console.log("Pressed")} didn't work..!!! onBackdropPress property was added since its version 3.xx and I was using 2.5.0 version.
So yarn update react-native-modal solved the issue.
If anyone encounters the problem that the library/component doesn't work as expected as you seen on documentation, try to check your package version number!
Cheers!
<Modal
isVisible={isModalVisible}
customBackdrop={
<View
style={styles.backDropContainer}
onTouchEnd={() => setModalVisible(false)}
/>
}
onBackdropPress={() => setModalVisible(false)}>
<View style={styles.modalContainer}>
<FlatList data={DATA} renderItem={() => <Text>hehehe</Text>} />
</View>
</Modal>
Style:
const styles = StyleSheet.create({
backDropContainer: {
flex: 1,
backgroundColor: 'black',
opacity: 0.5,
},
modalContainer: {
flex: 0.5,
backgroundColor: 'white',
padding: 10,
borderRadius: 10,
},
});
As stated if I use a custom navBar in react-native-router-flux, and style it with NO position stlyle tag, then the navBar renders at the bottom of the screen...
So, I tried setting style with position: 'absolute', top: 0, right:0, left: 0 and my navBar disappears :( Any suggestions?
Router.js
<Scene
key="api"
hideNavBar={false}
navBar={HeaderWithIcon}
component={APITest}
/>
HeaderWithIcon.js
import React, {Component} from 'react';
import { View, Image, Text } from 'react-native';
import { menuStyles } from './styles';
class HeaderWithIcon extends Component {
constructor(props) {
super(props);
}
render() {
const icon =
require('../../assets/images/myImg.png');
return (
<View>
<View style={[menuStyles.view, this.props.style]}>
<Image source={icon} style={[menuStyles.icon, this.props.iconStyle]} />
</View>
</View>
);
}
};
export default HeaderWithIcon;
styles.js
import { HEADER } from '../../global/margins';
import { PRIMARY_COLOUR, SHADOW_COLOUR } from '../../global/colours';
export const menuStyles = {
view: {
flexDirection: 'row',
//height: HEADER.height,
width: null,
backgroundColor: PRIMARY_COLOUR,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
// paddingTop: HEADER.padding,
shadowColor: SHADOW_COLOUR,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
position: 'absolute',
top: 0,
left: 0,
right: 0
},
icon: {
width: HEADER.icon,
height: HEADER.icon,
alignSelf: 'center'
}
};
<View style={styles.navbarStyle}>
<View style={styles.navContainer}>
<TouchableOpacity onPress={()=>Actions.get('drawer').ref.toggle()}>
<Image source={require('../../img/menu.png')} resizeMode="contain" color="white" style={styles.menu} />
</TouchableOpacity>
<View style={styles.navbarTitle}>
<Text style={styles.title}>NavBar</Text>
</View>
<TouchableWithoutFeedback onPress={this.loginUser}>
<View style={styles.signIn}>
<Text style={styles.title}>Login</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
And in order to style it and put it at the top ,remember to give it a position of 'absolute ' and 'top' of 0 :
navbarStyle:{
flex:1,
flexDirection:'row',
alignItems: 'center',
height ,
position: 'absolute', //necessary because if no react-native-router-flux will put navbar at bottom ,bug maybe!
top:0,
width,
backgroundColor: '#009688',
elevation:10
}
PS: for width and height ,better solution is to use Navigator and dimensions :
const {width} = Dimensions.get('window');
const height = Navigator.NavigationBar.Styles.General.NavBarHeight;
Nevermind... I realise that my HeaderView is wrapped in 2 views and I was not styling the outer one but the inner one. Leaving this here as example code for others if allowed.....
Fix:
In HeaderWithIcon.js
return (
<View style={[menuStyles.view, this.props.style]}>
<Image source={icon} style={[menuStyles.icon, this.props.iconStyle]} />
</View>
);