Skip to content
This repository has been archived by the owner on Feb 25, 2020. It is now read-only.

Commit

Permalink
feat: hide tab bar when keyboard is shown (#112)
Browse files Browse the repository at this point in the history
Closes #16

When the statusbar is not translucent, the view resizes when the keyboard is shown on Android. The tab bar stays above the keyboard. This PR makes the tab bar hide automatically when the keyboard is shown.

The behaviour is enabled by default and can be disabled with `keyboardHidesTabBar: false` in `tabBarOptions`
  • Loading branch information
satya164 committed Apr 1, 2019
1 parent 5d84d32 commit 791ecf2
Showing 1 changed file with 141 additions and 56 deletions.
197 changes: 141 additions & 56 deletions src/views/BottomTabBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TouchableWithoutFeedback,
StyleSheet,
View,
Keyboard,
Platform,
} from 'react-native';
import { SafeAreaView } from '@react-navigation/native';
Expand All @@ -14,6 +15,7 @@ import CrossFadeIcon from './CrossFadeIcon';
import withDimensions from '../utils/withDimensions';

export type TabBarOptions = {
keyboardHidesTabBar: boolean,
activeTintColor?: string,
inactiveTintColor?: string,
activeBackgroundColor?: string,
Expand Down Expand Up @@ -45,6 +47,12 @@ type Props = TabBarOptions & {
safeAreaInset: { top: string, right: string, bottom: string, left: string },
};

type State = {
layout: { height: number, width: number },
keyboard: boolean,
visible: Animated.Value,
};

const majorVersion = parseInt(Platform.Version, 10);
const isIos = Platform.OS === 'ios';
const isIOS11 = majorVersion >= 11 && isIos;
Expand Down Expand Up @@ -79,8 +87,9 @@ class TouchableWithoutFeedbackWrapper extends React.Component<*> {
}
}

class TabBarBottom extends React.Component<Props> {
class TabBarBottom extends React.Component<Props, State> {
static defaultProps = {
keyboardHidesTabBar: true,
activeTintColor: '#007AFF',
activeBackgroundColor: 'transparent',
inactiveTintColor: '#8E8E93',
Expand All @@ -92,6 +101,66 @@ class TabBarBottom extends React.Component<Props> {
safeAreaInset: { bottom: 'always', top: 'never' },
};

state = {
layout: { height: 0, width: 0 },
keyboard: false,
visible: new Animated.Value(1),
};

componentDidMount() {
if (Platform.OS === 'ios') {
Keyboard.addListener('keyboardWillShow', this._handleKeyboardShow);
Keyboard.addListener('keyboardWillHide', this._handleKeyboardHide);
} else {
Keyboard.addListener('keyboardDidShow', this._handleKeyboardShow);
Keyboard.addListener('keyboardDidHide', this._handleKeyboardHide);
}
}

componentWillUnmount() {
if (Platform.OS === 'ios') {
Keyboard.removeListener('keyboardWillShow', this._handleKeyboardShow);
Keyboard.removeListener('keyboardWillHide', this._handleKeyboardHide);
} else {
Keyboard.removeListener('keyboardDidShow', this._handleKeyboardShow);
Keyboard.removeListener('keyboardDidHide', this._handleKeyboardHide);
}
}

_handleKeyboardShow = () =>
this.setState({ keyboard: true }, () =>
Animated.timing(this.state.visible, {
toValue: 0,
duration: 150,
useNativeDriver: true,
}).start()
);

_handleKeyboardHide = () =>
Animated.timing(this.state.visible, {
toValue: 1,
duration: 100,
useNativeDriver: true,
}).start(() => {
this.setState({ keyboard: false });
});

_handleLayout = e => {
const { layout } = this.state;
const { height, width } = e.nativeEvent.layout;

if (height === layout.height && width === layout.width) {
return;
}

this.setState({
layout: {
height,
width,
},
});
};

_renderLabel = ({ route, focused }) => {
const {
activeTintColor,
Expand Down Expand Up @@ -202,6 +271,7 @@ class TabBarBottom extends React.Component<Props> {
render() {
const {
navigation,
keyboardHidesTabBar,
activeBackgroundColor,
inactiveBackgroundColor,
onTabPress,
Expand All @@ -222,62 +292,71 @@ class TabBarBottom extends React.Component<Props> {
];

return (
<SafeAreaView style={tabBarStyle} forceInset={safeAreaInset}>
{routes.map((route, index) => {
const focused = index === navigation.state.index;
const scene = { route, focused };

const accessibilityLabel = this.props.getAccessibilityLabel({
route,
});

const accessibilityRole =
this.props.getAccessibilityRole({
<Animated.View
style={[
styles.container,
keyboardHidesTabBar
? {
// When the keyboard is shown, slide down the tab bar
transform: [
{
translateY: this.state.visible.interpolate({
inputRange: [0, 1],
outputRange: [this.state.layout.height, 0],
}),
},
],
// Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden
position: this.state.keyboard ? 'absolute' : null,
}
: null,
]}
pointerEvents={
keyboardHidesTabBar && this.state.keyboard ? 'none' : 'auto'
}
onLayout={this._handleLayout}
>
<SafeAreaView style={tabBarStyle} forceInset={safeAreaInset}>
{routes.map((route, index) => {
const focused = index === navigation.state.index;
const scene = { route, focused };
const accessibilityLabel = this.props.getAccessibilityLabel({
route,
}) || 'button';

let accessibilityStates = this.props.getAccessibilityStates({
route,
});

if (!accessibilityStates) {
accessibilityStates = focused ? ['selected'] : [];
}

const testID = this.props.getTestID({ route });

const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;

const ButtonComponent =
this.props.getButtonComponent({ route }) ||
TouchableWithoutFeedbackWrapper;

return (
<ButtonComponent
key={route.key}
onPress={() => onTabPress({ route })}
onLongPress={() => onTabLongPress({ route })}
testID={testID}
accessibilityLabel={accessibilityLabel}
accessibilityRole={accessibilityRole}
accessibilityStates={accessibilityStates}
style={[
styles.tab,
{ backgroundColor },
this._shouldUseHorizontalLabels()
? styles.tabLandscape
: styles.tabPortrait,
tabStyle,
]}
>
{this._renderIcon(scene)}
{this._renderLabel(scene)}
</ButtonComponent>
);
})}
</SafeAreaView>
});
const testID = this.props.getTestID({ route });

const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;

const ButtonComponent =
this.props.getButtonComponent({ route }) ||
TouchableWithoutFeedbackWrapper;

return (
<ButtonComponent
key={route.key}
onPress={() => onTabPress({ route })}
onLongPress={() => onTabLongPress({ route })}
testID={testID}
accessibilityLabel={accessibilityLabel}
style={[
styles.tab,
{ backgroundColor },
this._shouldUseHorizontalLabels()
? styles.tabLandscape
: styles.tabPortrait,
tabStyle,
]}
>
{this._renderIcon(scene)}
{this._renderLabel(scene)}
</ButtonComponent>
);
})}
</SafeAreaView>
</Animated.View>
);
}
}
Expand All @@ -292,6 +371,12 @@ const styles = StyleSheet.create({
borderTopColor: 'rgba(0, 0, 0, .3)',
flexDirection: 'row',
},
container: {
left: 0,
right: 0,
bottom: 0,
elevation: 8,
},
tabBarCompact: {
height: COMPACT_HEIGHT,
},
Expand Down

0 comments on commit 791ecf2

Please sign in to comment.