I've followed a couple of good tutorials in using Layout Animation with react native. I cannot seem to get something so basic to animate. I am attempting to animate the Tab, when it has been clicked on, the flex size increase but in the mobile device, although the size does increase, it is static and no animation is applied. I'm testing this on an Android device.
Nav.js
import React from 'react';
import { View, LayoutAnimation } from 'react-native';
export class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 0
}
}
onTabPress(index) {
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ active: index });
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
render() {
return (
<View style={{ height: 56, elevation: 8, position: 'absolute', left: 0, bottom: 0, right: 0, backgroundColor: this.props.color, flex: 1, flexDirection: 'row' }}>
{
React.Children.map(this.props.children, (child, index) => (
React.cloneElement(child, {
index: index,
active: this.state.active === index,
onTabPress: this.onTabPress.bind(this),
})
))
}
</View>
);
}
}
Tab.js (this.props.children as shown above are a list of Tabs)
import React from 'react';
import { View, Text, TouchableWithoutFeedback, StyleSheet, Animated, Easing, Platform, LayoutAnimation } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
export class Tab extends React.Component {
constructor(props) {
super(props);
}
componentWillUpdate() {
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
handlePress() {
if (this.props.active) return;
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.props.onTabPress(this.props.index);
// LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
render() {
var active = this.props.active ? { flex: 1.75, top: 6 } : { flex: 1, top: 15 };
return (
<TouchableWithoutFeedback onPress={this.handlePress.bind(this)}>
<View style={[active, { alignItems: "center" }]}>
<Icon size={this.props.iconSize || 24} color={this.props.color || "white"} name={this.props.iconName} />
<Text style={{ color: this.props.color || "white" }}>{this.props.active && this.props.title}</Text>
</View>
</TouchableWithoutFeedback >
);
}
}
I've commented out the LayoutAnimation code blocks to clarify the areas where I have tried. I know i may be doing it wrong but I have followed these guides and it didn't work, so this code here was my attempt at trying to make it work my own way i guess. Thanks in advance.
https://blog.callstack.io/react-native-animations-revisited-part-i-783143d4884
https://medium.com/#Jpoliachik/react-native-s-layoutanimation-is-awesome-4a4d317afd3e
Well, seems that there was nothing wrong with my code. I went on http://snack.expo.io
to test this on ios and the animation worked. When moved to android, it was acting up exactly the way it was for my android device. A quick google search after that lead me to this.
https://github.com/facebook/react-native/issues/5267.
Basically, you just write the following in the constructor of your code and it will work. You can perform further checks on it too to define that this code would only run on android by using Platform.OS and an if statement
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
}
Related
I'm working on an app in React Native. I want to make it so the styling of the panel labels on the bottom of the screen updates based on the panel the user is on.
As of now, I can get the index of the current panel that's showing, but I don't know how to make that update the styling of the labels.
The first panel
and the second panel
Basically when you use the same component and want to style it in different ways, your component's style property depends on either props or state. There are many ways to put different styles depending on your state/props, I'll provide just a few of them:
Ternary operation based on current state.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const styles = StyleSheet.create({ // Your styles go here
labelFirstTab: {
textColor: 'red',
},
labelSecondTab: {
textColor: 'blue',
},
});
class MyApp extends React.Component {
state = {
currentTab: 0,
};
getLabelStyles = () => {
const { currentTab } = this.state;
return currentTab === 0 ? styles.labelFirstTab : styles.labelSecondTab;
};
render() {
return (
<View style={{ flex: 1 }}>
{/* Let's say this is your label */}
<Text style={this.getLabelStyles()}>Hi! I'm a nice label.</Text>
</View>
);
}
}
Additional styling based on props (also could depend on state, it doesn't matter).
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
label: {
fontSize: 16,
textColor: 'black',
},
labelDarkMode: {
textColor: 'lightgrey',
},
}); // Your styles go here
class MyApp extends React.Component {
getLabelStyles = () => {
const { isDarkMode } = this.props;
const { label, labelDarkMode } = styles;
return [styles.label, isDarkMode && styles.labelDarkMode];
};
render() {
return (
<View style={{ flex: 1 }}>
{/* Let's say this is your label */}
<Text style={this.getLabelStyles()}>Hi! I'm a nice label.</Text>
</View>
);
}
}
You even can pass your styles directly from your props, leaving the whole logic to your parent component. Just make sure that you've passed styles from your parent component to the current one.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
class MyApp extends React.Component {
render() {
const { labelStyles } = this.props;
return (
<View style={{ flex: 1 }}>
{/* Let's say this is your label */}
<Text style={labelStyles}>Hi! I'm a nice label.</Text>
</View>
);
}
}
Thank you E. Dn! This worked! Here's what I did.
const [activePanel, setActivePanel] = useState('nowPanel');
Swiping between panels calls a function:
const swipeNavigation = (index) => {
if (index === 0) {
setActivePanel('nowPanel');
} else {
setActivePanel('todayPanel');
}
};
Then within the actual View I want to style:
style={activePanel === 'nowPanel' ? styles.activePanel : null,}>
There are many options on how to blur an image in react native, but I want to blur a View component (or any other component, for what it's worth), not an Image.
How could I do that?
You can use react-native-blur to make any View Blur in react native
Try this
import React, { Component } from "react";
import { View, Image, Text, findNodeHandle, StyleSheet } from "react-native";
import { BlurView } from "#react-native-community/blur";
export default class Menu extends Component {
constructor(props) {
super(props);
this.state = { viewRef: null };
}
imageLoaded() {
this.setState({ viewRef: findNodeHandle(this.backgroundImage) });
}
render() {
return (
<View style={styles.container}>
<Text>Hi, I am some unblurred text</Text>
<BlurView
style={styles.absolute}
viewRef={this.state.viewRef}
blurType="light"
blurAmount={10}
/>
<Image
ref={img => {
this.backgroundImage = img;
}}
source={{ uri }}
style={styles.absolute}
onLoadEnd={this.imageLoaded.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: "center",
alignItems: "center"
},
absolute: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0
}
});
I am trying to learn React Native and want to make text that changes color every second. I have this code, but my ios emulator just shows a blank white screen with no text at all. Can someone take a look at the code below and tell me what I did wrong?
Thank You!
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<ChangeColor text= 'This text should be changing color.'/>
<ChangeColor text= 'Hopefully it works.'/>
</View>
);
}
}
class ChangeColor extends React.Component {
constructor(props) {
super(props);
this.state = {color: StyleSheet.skyBlue};
// Toggle the state every second
setInterval(() => {
this.setState(
{ color: StyleSheet.steelBlue}
);
}, 1000);
}
render() {
let display = this.state.color ? this.props.text : ' ';
return (
<Text>{display}</Text>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
skyBlue: {
color: 'skyblue',
},
steelBlue: {
color: 'steelblue',
},
});
Take a look at this snack: https://snack.expo.io/rkFe9tpfQ
StyleSheet.steelBlue
becomes
styles.steelBlue
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
What is the best module out there right now for image viewing? I wanted to find something like https://github.com/oblador/react-native-lightbox
which has swipe to dismiss features and such. But this module seems to be out of date. Anyone here use anything similar that works with latest version of React Native?
Something I can use to view images, pinch to zoom, swipe to dismiss are kinda essential for my app.
First you must know about routerFlux . then go to api and read the LightBox components...
so here is the simple codes that you make a boxlight:
import React from 'react';
import { View , Text } from 'react-native';
import { Router , Scene , Lightbox} from 'react-native-router-flux';
// Components
import ButtonPage from "./components/ButtonPage";
import LoginLightbox from "./components/lightbox/LoginLightbox";
class loginLightbox extends React.Component {
render() {
return (
<View style={{ flex : 1 , justifyContent: 'center' , alignItems: 'center'}}>
<Text>lightBox</Text>
</View>
)
}
}
export default class App extends React.Component {
render() {
return (
<Router>
<Lightbox>
<Scene key="root">
<Scene key="button" component={ButtonPage } title="ButtonPage " initial/>
</Scene>
<Scene key="loginLightbox" component={loginLightbox} />
</Lightbox>
</Router>
)
}
}
and this is the ButtonPage:
import React from 'react';
import { Container , Button } from 'native-base';
import { Actions } from 'react-native-router-flux';
import { form } from './../assets/styles';
export default class ButtonPage extends React.Component {
render() {
return (
<Container>
<Button full style={form.submitButton} onPress={() => Actions.loginLightbox()}>
<Text style={form.submitText}>ورود</Text>
</Button>
</Container>
)
}
}
now lets make tow class BaseLightBox:
import React from 'react';
import { Animated , Dimensions } from 'react-native';
import {View, Button, Text, Icon} from 'native-base';
import EStyleSheet from 'react-native-extended-stylesheet';
import { Actions } from 'react-native-router-flux';
const { height : deviceHeight , width : deviceWidth} = Dimensions.get('window');
export default class BaseLightbox extends React.Component {
constructor(props) {
super(props);
this.state = {
opacity : new Animated.Value(0)
}
}
componentWillMount() {
Animated.timing(this.state.opacity,{
toValue : 1,
duration : 200
}).start();
}
close() {
Animated.timing(this.state.opacity,{
toValue : 0,
duration : 200
}).start(Actions.pop);
}
_renderLightbox() {
const { children , verticalPercent = 1 , horizontalPercent = 1 } = this.props;
const width = verticalPercent ? deviceWidth * verticalPercent : deviceWidth;
const height = horizontalPercent ? deviceHeight * horizontalPercent : deviceHeight;
return (
<View style={{ width , height, justifyContent: 'center' , alignItems: 'center' , backgroundColor : 'white' , borderRadius : 4}}>
{children}
<Button transparent style={{ position: 'absolute', top : 0 , left : 0}} onPress={() => this.close() }>
<Icon name='md-close-circle' style={{ fontSize : 30 , color : '#34495e'}}/>
</Button>
</View>
)
}
render() {
return (
<Animated.View style={[styles.container , { opacity : this.state.opacity }]}>
{this._renderLightbox()}
</Animated.View>
)
}
}
const styles = EStyleSheet.create({
container : {
backgroundColor: 'rgba(52,52,52,.5)',
position: 'absolute',
top : 0 ,
bottom : 0,
left : 0,
right : 0,
justifyContent: 'center',
alignItems: 'center'
}
})
and LoginLightBox:
import React from 'react';
import { Animated , Text } from 'react-native';
import BaseLightbox from "./BaseLightbox";
export default class LoginLightbox extends React.Component {
render() {
return (
<BaseLightbox verticalPercent={0.7} horizontalPercent={0.5}>
<Text>Welcome to roocket</Text>
<Text>Learn React native</Text>
</BaseLightbox>
)
}
}
I'm playing with react native and got a strange behaviour.
When I try to show a ActitvityIndicator for Android setting its animating property to true with a showProgress variable in the state it doesn't work if the variable is started as false.
In the sample below if the ActivityIndicator animating property start as true, then the buttons make the ActivityIndicator hide or appear correctly.
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TextInput,
TouchableHighlight,
ActivityIndicator
} from 'react-native';
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: true
};
}
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator animating={this.state.showProgress} size="large"/>
</View>
);
}
progressOff() {
this.setState({showProgress: false});
}
progressOn() {
this.setState({showProgress: true});
}
}
But if i use the code below, with the animating property starting as false, then the button to make the ActivityIndicator appear doesn't work:
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TextInput,
TouchableHighlight,
ActivityIndicator
} from 'react-native';
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: false
};
}
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator animating={this.state.showProgress} size="large"/>
</View>
);
}
progressOff() {
this.setState({showProgress: false});
}
progressOn() {
this.setState({showProgress: true});
}
}
What am I missing here?
This appears to be a bug in React Native. The code with initial state being showProgress: false works on iOS but not on Android.
I've opened an issue on github if you want to follow the progression:
https://github.com/facebook/react-native/issues/9023
Option 1
A workaround I've used is to use the showProgress variable to render a completely different view with the ActivityIndicator:
render() {
if (this.state.showProgress) {
return this.renderLoadingView();
} else {
return this.renderMainView();
}
}
Option 2
You can also set the opacity of the ActivityIndicator according to the state:
render() {
return (
<View>
<TouchableHighlight onPress={this.progressOff.bind(this)}>
<Text>progressOff</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.progressOn.bind(this)}>
<Text>progressOn</Text>
</TouchableHighlight>
<ActivityIndicator style={{opacity: this.state.showProgress ? 1.0 : 0.0}} animating={true} size="large"/>
</View>
);
}
However the spinner animation doesn't always start at the same position when using this method.
This is a bug of React-Native for component Activity Indicator.
I am not sure that fb has already solved it but you can try this
constructor(props) {
super(props);
this.state = {
opacity: 0
};
}
to show it use this.setState({opacity:1}) and to hide again this.setState({opacity:0}) in your called functions
and in the render where you are using activity indicator
<ActivityIndicator
animating={true}
color="#ffffff"
style={{height: 80, marginTop: 10, opacity: this.state.opacity }}
size="large"/>
If in your project you can use third party components, I recommend the use of react-native-loading-spinner-overlay
Solved easily our problems, beacause this component use a similar way to show or hide the Activity with the property visible.
Another way I found effective to work around that problem without much code is:
{ this.state.showProgress &&
<ActivityIndicator animating={true} size="large"/>
}
I tried a different approach which I think that it is a more "react way" to solve problems. So, the problems with the opacity solution is that If you just set it to 0, it still will be a animation, so it is not the best solution thinking in your app performance.
I created a separated component that I called <Loading/>, here is the code:
import { ActivityIndicator } from "react-native"
import React from "react"
import PropTypes from "prop-types"
const Loading = (props) =>
props.animating
? <ActivityIndicator style={props.style}
importantForAccessibility='auto' size={props.size}
color={props.size} /> : null
Loading.propTypes = {
animating: PropTypes.bool.isRequired,
style: PropTypes.oneOfType([PropTypes.style, PropTypes.object]),
}
export default Loading
Usage:
<Loading animating={true} importantForAccessibility='auto' size="large" color="#A02BFF" style={styles.loading} />
That way it will avoid to create a animation when it is not a necessity, you will create separated component that can be removed easily at the point that the ActivityIndicator issue becomes solved in the future by replacing it to the original ActivityIndicator native component.
The only problem I had with this, was that in Android it wasn't visible because of the background I had on my screen. I fixed by only changing the color prop to something I knew should stand out in the background:
<ActivityIndicator color={theme.secondary.color} />
i got this problem all by a mistake. i did not put ActivityIndeicator in the center of a view. so it positioned on top of a view, which is covered by a natigation bar. code below is correct. hope this can help u.
<View style={{alignItems: 'center', justifyContent: 'center', flex: 1, backgroundColor: 'white'}}>
<ActivityIndicator
animating={true}
style={
{
alignItems: 'center',
justifyContent: 'center',
opacity: this.state.loading ? 1 : 0
}}
size="large"
/>
</View>
A quick fix Use conditional rendering.. Keep animating : {true} and just Visible and invisible view.
Checkout :
https://kylewbanks.com/blog/how-to-conditionally-render-a-component-in-react-native
In my case, for react native version 0.59.10 , the size property type is different for Android and iOS, so for that I had to make a Platform check as following and it worked.
<ActivityIndicator
size={Platform.OS === "ios" ? 0 : "large"} //This platform check worked.
color={props.color}
animating={props.animating}
style={props.style}
/>
The transition of animating from false to true is too slow on Android. But you can force a re-render using the key prop:
<ActivityIndicator
key={`${props.animating}`}
animating={props.animating}
/>
When props.animating changes from false to true, they key also changes. This forces a re-render, meaning that a new component is rendered with animating = true, which will instantly start your spinner.
If you are testing it on Android one of the reason could be the color property.
Be sure to give the ActivityIndicator a color. For example:
<ActivityIndicator size="large" color="#0000ff" />
This solution work perfectly for me in Android.
Hope this will help you.
import {ActivityIndicator} from 'react-native';
const [opacity, setOpacity] = useState(0)
const onLoadStart = () => {
setOpacity(1);
};
const onLoad = () => {
setOpacity(0);
};
const onBuffer = ({isBuffering}) => {
setOpacity(isBuffering ? 1 : 0);
};
return(
<View>
<Video
video={{uri: props.videoSource}}
autoplay={false}
customStyles={{
seekBarProgress: {
backgroundColor: theme.color.primary,
},
seekBarKnob: {
backgroundColor: theme.color.primary,
},
}}
ref={ref => (player = ref)}
onBuffer={onBuffer}
onLoadStart={onLoadStart}
onLoad={onLoad}
/>
<ActivityIndicator
animating
size="large"
color={color.primarylight}
style={{
opacity: opacity,
position: 'absolute',
top: 70,
left: 70,
right: 70,
// height: 50,
}}
/>
</View>
)