kirimi
kirimi

Reputation: 1400

react-native make status bar 'barStyle' animatable

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

Answers (3)

Max
Max

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

Muhammad Numan
Muhammad Numan

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",
  },
});

iphone x iphone

Upvotes: 1

Aswin C
Aswin C

Reputation: 1222

We can only animate the status bar by using a variable.(by toggling)

enter image description here

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

Related Questions