Permalink
Browse files

NavigationExperimental: Stop using absolute position for NavigationHe…

…ader.

Summary:
Not a API change, but this may break the layout of exisitng apps that
uses NavigationHeader.

For now, NavigationHeader uses absolute position, which makes it hard for
NavigationCardStack to determine the height of the scenes.

Theoretically, the height of the scenes would be the height of the cards
stack minus the height of the header.

That said, if we want to support the headers with different height (e.g.
MyIOSHeader or MyAndroidHeader), we're forced to expose the height of the
headers and manually compute the height of the scenes.

Alternatively, if the header does not use absolute position, the height
of the scenes can adjust automatically with flex box, and that's what this
commit is about to do.

Reviewed By: ericvicenti

Differential Revision: D3671119

fbshipit-source-id: 26e48f801da3661c5d7dce4752ba927621172f4a
  • Loading branch information...
1 parent 158d435 commit 38979f9c68bd054aca3c6102635b2005914506b1 @hedgerwang hedgerwang committed with Facebook Github Bot 2 Aug 4, 2016
@@ -359,7 +359,7 @@ const YourScene = createAppNavigationContainer(class extends Component {
render(): ReactElement {
return (
- <ScrollView style={styles.scrollView}>
+ <ScrollView>
<NavigationExampleRow
text="Push Route"
onPress={this._pushRoute}
@@ -463,9 +463,6 @@ const styles = StyleSheet.create({
navigatorCardStack: {
flex: 20,
},
- scrollView: {
- marginTop: 64
- },
tabs: {
flex: 1,
flexDirection: 'row',
@@ -165,7 +165,7 @@ class YourNavigator extends React.Component {
class YourScene extends React.Component {
render() {
return (
- <ScrollView style={styles.scrollView}>
+ <ScrollView>
<NavigationExampleRow
text={'route = ' + this.props.route.key}
/>
@@ -190,9 +190,6 @@ const styles = StyleSheet.create({
navigator: {
flex: 1,
},
- scrollView: {
- marginTop: 64
- },
});
module.exports = YourApplication;
@@ -194,7 +194,6 @@ const styles = StyleSheet.create({
},
exampleContainer: {
flex: 1,
- paddingTop: NavigationHeader.HEIGHT,
},
});
@@ -75,8 +75,8 @@ type DefaultProps = {
* A controlled navigation view that renders a stack of cards.
*
* +------------+
- * +-+ |
- * +-+ | |
+ * +-| Header |
+ * +-+ |------------|
* | | | |
* | | | Focused |
* | | | Card |
@@ -135,7 +135,7 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
renderHeader
} = this.props;
- const overlay = renderHeader && renderHeader(props);
+ const header = renderHeader ? <View>{renderHeader(props)}</View> : null;
const scenes = props.scenes.map(
scene => this._renderScene({
@@ -145,13 +145,12 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
);
return (
- <View
- style={styles.container}>
+ <View style={styles.container}>
<View
style={styles.scenes}>
{scenes}
</View>
- {overlay}
+ {header}
</View>
);
}
@@ -187,6 +186,11 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
const styles = StyleSheet.create({
container: {
flex: 1,
+ // Header is physically rendered after scenes so that Header won't be
+ // covered by the shadows of the scenes.
+ // That said, we'd have use `flexDirection: 'column-reverse'` to move
+ // Header above the scenes.
+ flexDirection: 'column-reverse',
},
scenes: {
flex: 1,
@@ -244,11 +244,6 @@ const styles = StyleSheet.create({
elevation: 4,
flexDirection: 'row',
justifyContent: 'flex-start',
- left: 0,
- marginBottom: 16, // This is needed for elevation shadow
- position: 'absolute',
- right: 0,
- top: 0,
},
title: {

8 comments on commit 38979f9

@chirag04
Contributor

if i understand correctly this will not let you do translucent headers anymore.

@lsps9150414

@chirag04 I guess in this case, to do translucent headers we need to set the scenes position to absolute and top: 0 instead.

@chirag04
Contributor

@lsps9150414 I haven't tried but with absolute position your scene will be on top of the header.

@bd-arc
bd-arc commented on 38979f9 Sep 26, 2016

@hedgerwang @lsps9150414 Unfortunately, I can confirm what @chirag04 mentioned. Scene's absolute positioning is a no-go on Android because of all the z-index issues: the scenes will remain on top of the header, meaning the latter will be invisible. (It can however be managed on iOS with some negative top margin).

Is there a workaround to create transparent headers on Android? Or are we stuck with plain ones?

@satya164
Contributor

@bd-arc You can pass style={{ position: 'absolute' }} I believe. And React Native has zIndex now, so you should be able to render it on top of the scene. Another trick is to use elevation. Higher elevation works like higher zIndex on Android.

@bd-arc
bd-arc commented on 38979f9 Sep 26, 2016

@satya164 Thank you for your very quick answer! Unfortunately, the solution you're suggesting doesn't work.

On both Android and iOS, it generates an empty space that cannot be filled at all. Even though the scene is absolutely positioned, the header somehow seems to prevent anything from being rendered beneath it.

My CardStack component:

<CardStack
  navigationState={this.props.navStack}
  onNavigateBack={this._onNavigateBack}
  enableGestures={true}
  renderScene={this._renderScene}
  renderHeader={this._renderHeader}
  style={styles.container}
  direction={this._animation}
/>

My scene:

<View style={{position: 'absolute', top: 0, left: 0, bottom: 0, right: 0}}>
    { this._renderStatusBar(sceneProps) }
    <Scene {...props} />
</View>

My header:

<Header
  {...this.props}
  style={{backgroundColor: 'transparent', elevation: 0}}
  renderTitleComponent={this._renderTitleComponent}
  renderLeftComponent={this._renderLeftComponent}
  renderRightComponent={this._renderRightComponent}
/>

The result:
Issue with transparent header

For the sake of completeness, I've also tried position: 'absolute' on the header. While this works on iOS, the header remains invisible on Android despite adding zIndex and/or elevation...

Any enlightenment would be really appreciated!

@satya164
Contributor

@bd-arc I was suggesting to add position absolute only on the header, not the scene. Looks like there's a <View /> wrapping the header which might be messing it up. I'm not sure why it's there. maybe @hedgerwang can provide some insight

@bd-arc
bd-arc commented on 38979f9 Sep 26, 2016

@satya164 Oh, ok; I did not get that. But anyway, as mentioned, I've tried it without success. I agree with you regarding the wrapping <View />; would love to hear more from @hedgerwang about that ;)

Please sign in to comment.