Reputation: 1400
I'm trying to make status bar's barStyle
animated so that when I scroll I could change the barStyle
type from 'dark-content' to 'light-content'.
First, I made my statusBar animated:
const AnimatedStatusBar = Animated.createAnimatedComponent(StatusBar);
Second, set Animated Value:
scroll = new Animated.Value(0);
tabY = this.nScroll.interpolate({
inputRange: [0, SCROLL_HEIGHT, SCROLL_HEIGHT],
outputRange: [0, 0, 1]
});
headerBg = this.scroll.interpolate({
inputRange: [0, SCROLL_HEIGHT, SCROLL_HEIGHT],
outputRange: ["black", 'white'],
extrapolate: "clamp"
});
Finally, change the barStyle if this.HeaderBg
has changed:
<AnimatedStatusBar barStyle={this.headerBg === 'transparent'? 'dark-content' : 'light-content'} translucent={true} />
I feel like my code should work but it is not working as I expected.
Any idea how I could change the type of the statusBar's barStyle
when animated has changed?
By the way I am not using hooks. I want to know how to do it with class components.
Upvotes: 2
Views: 2765
Reputation: 4749
StatusBar
is not an actual component that renders status bar. It is more of a convenient side-effect component that, when rendered, will make imperative call to set status bar to color that was passed in the prop. For this reason you can't animate it as it does not render anything
Also, you couldn't possibly animate between "light-content" and "dark-content", as one is white text on dark background and another is dark text on white background, there are no intermediate values between them
What you can do is you can change background color from one to another (without interpolating!). You can implement it like this using only imperative api
class App extends React.Component {
lastColor = null
componentDidMount() {
this.nScroll.addListener(({ value }) => {
if (value < SCROLL_HEIGHT) {
this.setStatusBarColor('black')
} else {
this.setStatusBarColor('white')
}
});
}
setStatusBarColor = (newColor) => {
if (newColor !== this.lastColor) {
StatusBar.setBackgroundColor(newColor)
}
}
///...
}
note in this case you don't need to render <StatusBar />
as all color changes are handled imperatively
If you want status bar to change color with interpolations, your only option is to set it translucent StatusBar.setTranslucent(true)
and then draw your own status bar with simple <View />
and animate its background color instead
Upvotes: 2
Reputation: 25413
you should use react-native-iphone-x-helper to get status bar height so you application will be work fine on iphoneX and normal iphone
Code:
import React, { Component } from "react";
import {
View,
StatusBar,
StyleSheet,
ScrollView,
Dimensions,
Animated,
} from "react-native";
const { height } = Dimensions.get("window");
import { getStatusBarHeight } from "react-native-iphone-x-helper";
const statusBarHeight = getStatusBarHeight();
export default class App extends Component {
scroll = new Animated.Value(0);
state = {
dark: true,
};
onScroll = ({ nativeEvent }) => {
let y = nativeEvent.contentOffset.y;
this.scroll.setValue(y); // set scroll animation value here
const { dark } = this.state;
let scrollValue = y;
if (scrollValue > statusBarHeight && dark) {
this.setState({ dark: false });
}
if (scrollValue < statusBarHeight && !dark) {
this.setState({ dark: true });
}
};
render() {
const { dark } = this.state;
return (
<View style={styles.container}>
<StatusBar
barStyle={dark ? "dark-content" : "light-content"}
translucent
backgroundColor="transparent"
/>
<ScrollView
style={styles.ScrollView}
scrollEventThrottle={16}
contentContainerStyle={styles.contentContainerStyle}
onScroll={this.onScroll}
>
<View style={styles.item} />
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
ScrollView: {
flex: 1,
},
contentContainerStyle: {
height: height * 2,
backgroundColor: "#000",
},
item: {
height: 100,
backgroundColor: "#FFFFFF",
},
});
Upvotes: 1
Reputation: 1222
We can only animate the status bar by using a variable.(by toggling)
let's take dark as the variable.
state = {
dark: true,
};
initially, it's true. so the status bar style should be dark.
<StatusBar
barStyle={dark ? 'dark-content' : 'light-content'}
translucent
backgroundColor="transparent"
/>
The toggle is done inside on scroll method. here I am using 100 as an offset.
onScroll = ({nativeEvent}) => {
let y = nativeEvent.contentOffset.y;
this.scroll.setValue(y); // set scroll animation value here
const {dark} = this.state;
let scrollValue = y;
if (scrollValue > 100 && dark) {
this.setState({dark: false});
}
if (scrollValue < 100 && !dark) {
this.setState({dark: true});
}
};
Example Code
import React, {Component} from 'react';
import {
View,
StatusBar,
StyleSheet,
ScrollView,
Dimensions,
Animated,
} from 'react-native';
const {height} = Dimensions.get('window');
export default class Home extends Component {
scroll = new Animated.Value(0);
state = {
dark: true,
};
onScroll = ({nativeEvent}) => {
let y = nativeEvent.contentOffset.y;
this.scroll.setValue(y); // set scroll animation value here
const {dark} = this.state;
let scrollValue = y;
if (scrollValue > 100 && dark) {
this.setState({dark: false});
}
if (scrollValue < 100 && !dark) {
this.setState({dark: true});
}
};
render() {
const {dark} = this.state;
return (
<View style={styles.container}>
<StatusBar
barStyle={dark ? 'dark-content' : 'light-content'}
translucent
backgroundColor="transparent"
/>
<ScrollView
style={styles.ScrollView}
contentContainerStyle={styles.scrollContainer}
onScroll={this.onScroll}>
<View style={styles.item} />
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
ScrollView: {
flex: 1,
},
scrollContainer: {
height: height * 2,
backgroundColor: '#000',
},
item: {
height: 100,
backgroundColor: '#fff',
},
});
Upvotes: 3