Change <Header /> layout back to absolute (#562)

This commit is contained in:
Mike Grabowski 2017-03-03 13:13:30 +01:00 committed by Satyajit Sahoo
parent bbab489a6a
commit e650f341e3
2 changed files with 69 additions and 52 deletions

View File

@ -58,6 +58,7 @@ type HeaderState = {
const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56; const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0; const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 40;
class Header extends React.PureComponent<void, HeaderProps, HeaderState> { class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
@ -121,8 +122,23 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
const titleStyle = this._getHeaderTitleStyle(props.navigation); const titleStyle = this._getHeaderTitleStyle(props.navigation);
const color = this._getHeaderTintColor(props.navigation); const color = this._getHeaderTintColor(props.navigation);
const title = this._getHeaderTitle(props.navigation); const title = this._getHeaderTitle(props.navigation);
// On iOS, width of left/right components depends on the calculated
// size of the title.
const onLayoutIOS = Platform.OS === 'ios'
? (e: LayoutEvent) => {
this.setState({
widths: {
...this.state.widths,
[props.key]: e.nativeEvent.layout.width,
},
});
}
: undefined;
return ( return (
<HeaderTitle <HeaderTitle
onLayout={onLayoutIOS}
style={[color ? { color } : null, titleStyle]} style={[color ? { color } : null, titleStyle]}
> >
{title} {title}
@ -140,11 +156,15 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
state: props.scenes[props.scene.index - 1].route, state: props.scenes[props.scene.index - 1].route,
}); });
const backButtonTitle = this._getBackButtonTitle(previousNavigation); const backButtonTitle = this._getBackButtonTitle(previousNavigation);
const width = this.state.widths[props.key]
? (props.layout.initWidth - this.state.widths[props.key]) / 2
: undefined;
return ( return (
<HeaderBackButton <HeaderBackButton
onPress={props.onNavigateBack} onPress={props.onNavigateBack}
tintColor={tintColor} tintColor={tintColor}
title={backButtonTitle} title={backButtonTitle}
width={width}
/> />
); );
}; };
@ -161,9 +181,20 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
); );
} }
_renderTitle(props: NavigationSceneRendererProps): ?React.Element<*> { _renderTitle(props: NavigationSceneRendererProps, options: *): ?React.Element<*> {
const style = {};
if (Platform.OS === 'android') {
if (!options.hasLeftComponent) {
style.left = 0;
}
if (!options.hasRightComponent) {
style.right = 0;
}
}
return this._renderSubView( return this._renderSubView(
props, { ...props, style },
'title', 'title',
this.props.renderTitleComponent, this.props.renderTitleComponent,
this._renderTitleComponent, this._renderTitleComponent,
@ -216,36 +247,20 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
subView = defaultRenderer(subViewProps); subView = defaultRenderer(subViewProps);
} }
const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none'; if (subView === null) {
return null;
// On iOS, width of left/right components depends on the calculated
// size of the title.
const onLayoutIOS = Platform.OS === 'ios' && name === 'title'
? (e: LayoutEvent) => {
this.setState({
widths: {
...this.state.widths,
[key]: e.nativeEvent.layout.width,
},
});
} }
: undefined;
const titleWidth = name === 'left' || name === 'right' const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none';
? this.state.widths[key]
: undefined;
return ( return (
<Animated.View <Animated.View
pointerEvents={pointerEvents} pointerEvents={pointerEvents}
onLayout={onLayoutIOS}
key={`${name}_${key}`} key={`${name}_${key}`}
style={[ style={[
titleWidth && {
width: (props.layout.initWidth - titleWidth) / 2,
},
styles.item, styles.item,
styles[name], styles[name],
props.style,
styleInterpolator(props), styleInterpolator(props),
]} ]}
> >
@ -257,15 +272,18 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
_renderHeader(props: NavigationSceneRendererProps): React.Element<*> { _renderHeader(props: NavigationSceneRendererProps): React.Element<*> {
const left = this._renderLeft(props); const left = this._renderLeft(props);
const right = this._renderRight(props); const right = this._renderRight(props);
const title = this._renderTitle(props); const title = this._renderTitle(props, {
hasLeftComponent: !!left,
hasRightComponent: !!right,
});
return ( return (
<View <View
style={[StyleSheet.absoluteFill, styles.header]} style={[StyleSheet.absoluteFill, styles.header]}
key={`scene_${props.scene.key}`} key={`scene_${props.scene.key}`}
> >
{left}
{title} {title}
{left}
{right} {right}
</View> </View>
); );
@ -289,7 +307,7 @@ class Header extends React.PureComponent<void, HeaderProps, HeaderState> {
appBar = scenesProps.map(this._renderHeader, this); appBar = scenesProps.map(this._renderHeader, this);
} else { } else {
appBar = this._renderHeader({ appBar = this._renderHeader({
...this.props, ...NavigationPropTypes.extractSceneRendererProps(this.props),
position: new Animated.Value(this.props.scene.index), position: new Animated.Value(this.props.scene.index),
progress: new Animated.Value(0), progress: new Animated.Value(0),
}); });
@ -329,19 +347,30 @@ const styles = StyleSheet.create({
}, },
item: { item: {
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent', backgroundColor: 'transparent',
}, },
title: Platform.OS === 'android' title: {
? { bottom: 0,
flex: 1, left: TITLE_OFFSET,
alignItems: 'flex-start', right: TITLE_OFFSET,
} top: 0,
: {}, position: 'absolute',
alignItems: Platform.OS === 'android'
? 'flex-start'
: 'center',
},
left: { left: {
alignItems: 'flex-start', left: 0,
bottom: 0,
top: 0,
position: 'absolute',
}, },
right: { right: {
alignItems: 'flex-end', right: 0,
bottom: 0,
top: 0,
position: 'absolute',
}, },
}); });

View File

@ -19,6 +19,7 @@ type Props = {
title?: ?string, title?: ?string,
tintColor?: ?string, tintColor?: ?string,
truncatedTitle?: ?string, truncatedTitle?: ?string,
width?: ?number,
}; };
type DefaultProps = { type DefaultProps = {
@ -27,7 +28,6 @@ type DefaultProps = {
}; };
type State = { type State = {
containerWidth?: number,
initialTextWidth?: number, initialTextWidth?: number,
}; };
@ -37,6 +37,7 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
title: PropTypes.string, title: PropTypes.string,
tintColor: PropTypes.string, tintColor: PropTypes.string,
truncatedTitle: PropTypes.string, truncatedTitle: PropTypes.string,
width: PropTypes.number,
}; };
static defaultProps = { static defaultProps = {
@ -48,15 +49,6 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
state = {}; state = {};
_onContainerLayout = (e: LayoutEvent) => {
if (Platform.OS !== 'ios') {
return;
}
this.setState({
containerWidth: e.nativeEvent.layout.width,
});
};
_onTextLayout = (e: LayoutEvent) => { _onTextLayout = (e: LayoutEvent) => {
if (this.state.initialTextWidth) { if (this.state.initialTextWidth) {
return; return;
@ -67,10 +59,10 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
}; };
render() { render() {
const { onPress, title, tintColor, truncatedTitle } = this.props; const { onPress, width, title, tintColor, truncatedTitle } = this.props;
const renderTruncated = this.state.containerWidth && this.state.initialTextWidth const renderTruncated = this.state.initialTextWidth && width
? this.state.containerWidth < this.state.initialTextWidth ? this.state.initialTextWidth > width
: false; : false;
return ( return (
@ -80,10 +72,7 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
style={styles.container} style={styles.container}
borderless borderless
> >
<View <View style={styles.container}>
onLayout={this._onContainerLayout}
style={styles.container}
>
<Image <Image
style={[ style={[
styles.icon, styles.icon,
@ -94,7 +83,6 @@ class HeaderBackButton extends React.PureComponent<DefaultProps, Props, State> {
/> />
{Platform.OS === 'ios' && title && ( {Platform.OS === 'ios' && title && (
<Text <Text
ellipsizeMode="middle"
onLayout={this._onTextLayout} onLayout={this._onTextLayout}
style={[styles.title, { color: tintColor }]} style={[styles.title, { color: tintColor }]}
numberOfLines={1} numberOfLines={1}