Going to use Expo for this since it provides a nice wrapper around the development tools and includes a lot of good libraries.

https://expo.io/learn

It wanted me to sign up for an Expo account so I did. I installed the Expo Client and was able to run the app!

Lets look at the day pass to identify the components

  1. The top navigation bar with the back arrow. For this example the back arrow doesn’t really need to do anything
  2. This is the most complex component. It shows a scrolling, color shifting timestamp bar. When the arrow is activated the bar is replaced with a QR code. We’ll need to constantly update the time and the date should be today’s date. The colors are supposed to shift but most of the time it is broken in my version of the app and shows 3 bars instead. Also, there is a white overlay that transitions in opacity from full transparent to slightly opaque.
  3. The “Tap to reveal barcode” function, which replaces the timestamp color bar with a QR code image, and slides the whole window down to fit.
  4. The rest of the page containing the pass type and the image, and the action buttons. We’ll use plain images for this example.

Let’s start by running the app on an open iOS simulator

exp start –ios

Here is how I wrote the component code. I started with the container Expo created and then added the four components from above.


<View style={styles.container}>
</View>

Here’s the top navigation. You can see I’m also using the styles object that Expo creates.


<View style={styles.headerBar}>
<Text style={styles.headerText}>Regional/Airport Day Pass</Text>
</View>

I gave my component a style for the view node and one for the text node since the view is used for positioning and the text is used for rendering text.

Next here’s the container for the scrolling text and the QR code


<View style={styles.timestampBar}>
{ timestampBar }
{ codeImage }
</View>

and the actual meat of the component


let codeImage = null, timestampBar = null;
if (this.state.isOpen) {
codeImage = (

<Image style={{width:300,height:300}} source={{uri: 'https://i.imgur.com/mc3xR7M.pngjpg'}} />
);
} else {
timestampBar = [(<View key={0} style={styles.colorContainerStyle}>
<View style={styles.color1}></View>
<View style={styles.color2}></View>
<View style={styles.color3}></View>
<View style={colorOverlayStyle}></View>
</View>),
(<Animated.View key={1} style={scrollingStyle}>
<View style={{position: 'absolute'}}>
<Text style={styles.timestampTextBg}>{moment(timestamp).format('MM/DD/YY')}</Text>
<Text style={styles.timestampTextBg}>{moment(timestamp).format('h:mm:ss A')}</Text>
</View>
<Text style={styles.timestampText}>{moment(timestamp).format('MM/DD/YY')}</Text>
<Text style={styles.timestampText}>{moment(timestamp).format('h:mm:ss A')}</Text>
</Animated.View>),
(<Text key={2} style={styles.revealText}>Tap to reveal barcode</Text>)];
}

The view will either show the timestampBar or the codeImage depending on this.state.isOpen. This is something I like to do in React where I conditionally render one view or another based on the component’s state.
Here I used an animated view to move the timestamp back and forth like the actual app does. Its animated on the “left” CSS property.
Something else I needed to deal with was the text shadow on the timestamp that encircles the entire text. React Native only supports one text shadow and I needed 2 to do this, so I actually am duplicating the timestamp text and putting one set inside a view that’s position:absolute. This puts it in the background of the other. Then in the CSS, I give one a positive text-shadow and the other a negative text-shadow. This accomplishes the look I want.
Finally notice that the color bar has three square colors and then an overlay on top. This overlay covers the entire area of the three color squares and has an opacity that animates to a white background color, so when combined with the colors behind it, the colors appear to brighten and then dim.

Here’s the tap to reveal component

<Animated.View style={{transform: [{translateY:translateY1}], width: width, alignItems: 'center'}}>
<TouchableWithoutFeedback onPress={this.toggle.bind(this)}>
<Image style={styles.revealButton} source={require('./assets/reveal-btn.png')}></Image>
</TouchableWithoutFeedback>
</Animated.View>

I’m using an easing animation on the view. The TouchableWithoutFeedback element is how I found to do a button with a background image – the actual Button element would not work for me.

Finally here is my entire App.js