Jo Ko Jo Ko - 2 months ago 13
CSS Question

How to from top that replaces status bar?

In React Native iOS, I would like to slide in and out a

<View/>
-- assuming it is implemented as such-- like the following pictures.

Answer

Here's the result:

enter image description here

Here's the full code to make it work. It will not work on RNPlay because zIndex is not supported in the version they have.

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  StatusBar,
  View,
  TouchableHighlight,
  Animated
} from 'react-native';

class playground extends Component {
  constructor(props) {
     super(props);
     this.state = {
       slideAnimation: new Animated.Value(22),
     };
   }

  _showNotification() {
    StatusBar.setHidden(true, 'slide');

    Animated.timing(
      this.state.slideAnimation,
      {toValue: 0, duration: 300}
    ).start();
  }

  _hideNotification() {
    StatusBar.setHidden(false, 'slide');

    Animated.timing(
      this.state.slideAnimation,
      {toValue: 22, duration: 300}
    ).start();
  }

  render() {
    return (
      <View style={styles.container}>
        <StatusBar
           barStyle="default"
         />
        <Animated.View style={[styles.notification, {top: this.state.slideAnimation}]}>
          <Text style={styles.notificationText}>This is a notification</Text>
        </Animated.View>
        <View style={styles.body}>
          <TouchableHighlight underlayColor="#D1EEFC" style={styles.button} onPress={()=> { this._showNotification() }}>
            <Text style={styles.buttonText}>
              Show Notification
            </Text>
          </TouchableHighlight>

          <TouchableHighlight underlayColor="#D1EEFC" style={styles.button} onPress={()=> { this._hideNotification() }}>
            <Text style={styles.buttonText}>
              Hide Notification
            </Text>
          </TouchableHighlight>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  body: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF', //This is important to hide the notification, because it is actually behind it
    marginTop: 22, //This will make a gap of the same height in the top, so that the notification can slide in it.
  },
  button: {
    padding: 10,
    borderColor: '#D1EEFC',
    borderWidth: 2,
    borderRadius: 5,
    marginBottom: 22,
  },
  buttonText: {
    fontSize: 17,
    textAlign: 'center',
    color: '#007AFF'
  },
  notification: {
    backgroundColor: 'black',
    position: 'absolute',
    top: 22, //Move the notification downwards to setup the scene.
    left: 0,
    right: 0,
    height: 22, //Make it the same height as the status bar
    zIndex: 0, //This is what makes the notification benhind the container
  },
  notificationText: {
    color: 'yellow',
    textAlign: 'center',
    fontSize: 11,
    marginTop: 4
  },
});

AppRegistry.registerComponent('playground', () => playground);

UPDATE

I've managed to make the masking concept work by wrapping the notification inside a view with the style overflow: 'hidden'. The container is set to height: 22. So if the notification moves even 1pt, it will look as if the notification is sliding-in the background.

Here's the result:

enter image description here

Here's the code:

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  StatusBar,
  View,
  TouchableHighlight,
  Animated,
  Image
} from 'react-native';

class playground extends Component {
  constructor(props) {
     super(props);
     this.state = {
       slideAnimation: new Animated.Value(22),
     };
   }

  _showNotification() {
    StatusBar.setHidden(true, 'slide');

    Animated.timing(
      this.state.slideAnimation,
      {toValue: 0, duration: 300}
    ).start();
  }

  _hideNotification() {
    StatusBar.setHidden(false, 'slide');

    Animated.timing(
      this.state.slideAnimation,
      {toValue: 22, duration: 300}
    ).start();
  }

  render() {
    return (
      <View style={styles.container}>
        <Image source={require('./img/splash.png')} style={styles.backgroundImage} resizeMode='cover' />
        <StatusBar
           barStyle="default"
         />
        <View style={styles.notificationContainer}>
          <Animated.View style={[styles.notification, {top: this.state.slideAnimation}]}>
            <Text style={styles.notificationText}>This is a notification</Text>
          </Animated.View>
        </View>
        <TouchableHighlight underlayColor="#D1EEFC" style={styles.button} onPress={()=> { this._showNotification() }}>
          <Text style={styles.buttonText}>
            Show Notification
          </Text>
        </TouchableHighlight>

        <TouchableHighlight underlayColor="#D1EEFC" style={styles.button} onPress={()=> { this._hideNotification() }}>
          <Text style={styles.buttonText}>
            Hide Notification
          </Text>
        </TouchableHighlight>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
    justifyContent: 'center',
    alignItems: 'center',
  },
  backgroundImage: {
    position: 'absolute',
    top: 0,
  },
  button: {
    padding: 10,
    borderRadius: 5,
    marginBottom: 22,
    backgroundColor: '#FFFFFF88',
  },
  buttonText: {
    fontSize: 17,
    textAlign: 'center',
    color: '#000000'
  },
  notificationContainer: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: 22,
    overflow: 'hidden' //This is the magic trick to do the masking.
  },
  notification: {
    backgroundColor: '#00000088',
    position: 'absolute',
    top: 22,
    left: 0,
    right: 0,
    height: 22,
  },
  notificationText: {
    color: 'yellow',
    textAlign: 'center',
    fontSize: 11,
    marginTop: 4
  },
});

AppRegistry.registerComponent('playground', () => playground);