mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-10 17:23:42 +08:00
feat: hide tab bar when keyboard is shown (#112)
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`
This commit is contained in:
committed by
satyajit.happy
parent
73e9b4cb8f
commit
ccb2d38089
@@ -2,18 +2,20 @@
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
TouchableWithoutFeedback,
|
||||
StyleSheet,
|
||||
View,
|
||||
Keyboard,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from '@react-navigation/native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import CrossFadeIcon from './CrossFadeIcon';
|
||||
import withDimensions from '../utils/withDimensions';
|
||||
|
||||
export type TabBarOptions = {
|
||||
keyboardHidesTabBar: boolean,
|
||||
activeTintColor?: string,
|
||||
inactiveTintColor?: string,
|
||||
activeBackgroundColor?: string,
|
||||
@@ -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;
|
||||
@@ -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',
|
||||
@@ -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,
|
||||
@@ -202,6 +271,7 @@ class TabBarBottom extends React.Component<Props> {
|
||||
render() {
|
||||
const {
|
||||
navigation,
|
||||
keyboardHidesTabBar,
|
||||
activeBackgroundColor,
|
||||
inactiveBackgroundColor,
|
||||
onTabPress,
|
||||
@@ -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';
|
||||
});
|
||||
const testID = this.props.getTestID({ route });
|
||||
|
||||
let accessibilityStates = this.props.getAccessibilityStates({
|
||||
route,
|
||||
});
|
||||
const backgroundColor = focused
|
||||
? activeBackgroundColor
|
||||
: inactiveBackgroundColor;
|
||||
|
||||
if (!accessibilityStates) {
|
||||
accessibilityStates = focused ? ['selected'] : [];
|
||||
}
|
||||
const ButtonComponent =
|
||||
this.props.getButtonComponent({ route }) ||
|
||||
TouchableWithoutFeedbackWrapper;
|
||||
|
||||
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>
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user