mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-24 17:18:09 +00:00
* Fixing iPad iOS 11 Tab Bar Bottom Behavior with changing widths via Multitasking * Updating snapshot tests * Changes per discussion in !3041 * Additional changes per discussion in !3041 * Changing to test for constrained height * Additional changes per discussion in !3041
This commit is contained in:
parent
e2e540c32d
commit
c7b73cd8b4
@ -105,68 +105,81 @@ exports[`TabNavigator renders successfully 1`] = `
|
|||||||
"alignItems": "center",
|
"alignItems": "center",
|
||||||
"backgroundColor": "rgba(0, 0, 0, 0)",
|
"backgroundColor": "rgba(0, 0, 0, 0)",
|
||||||
"flex": 1,
|
"flex": 1,
|
||||||
"justifyContent": "flex-end",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testID={undefined}
|
testID={undefined}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"flexGrow": 1,
|
Object {
|
||||||
}
|
"alignItems": "center",
|
||||||
|
"flex": 1,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"flexDirection": "column",
|
||||||
|
"justifyContent": "flex-end",
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
collapsable={undefined}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"flexGrow": 1,
|
||||||
"bottom": 0,
|
|
||||||
"justifyContent": "center",
|
|
||||||
"left": 0,
|
|
||||||
"opacity": 1,
|
|
||||||
"position": "absolute",
|
|
||||||
"right": 0,
|
|
||||||
"top": 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<View
|
<View
|
||||||
|
collapsable={undefined}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"alignItems": "center",
|
||||||
|
"bottom": 0,
|
||||||
|
"justifyContent": "center",
|
||||||
|
"left": 0,
|
||||||
|
"opacity": 1,
|
||||||
|
"position": "absolute",
|
||||||
|
"right": 0,
|
||||||
|
"top": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
collapsable={undefined}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"alignItems": "center",
|
||||||
|
"bottom": 0,
|
||||||
|
"justifyContent": "center",
|
||||||
|
"left": 0,
|
||||||
|
"opacity": 0,
|
||||||
|
"position": "absolute",
|
||||||
|
"right": 0,
|
||||||
|
"top": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text
|
||||||
|
accessible={true}
|
||||||
|
allowFontScaling={true}
|
||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
|
ellipsizeMode="tail"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"backgroundColor": "transparent",
|
||||||
"bottom": 0,
|
"color": "rgba(52, 120, 246, 1)",
|
||||||
"justifyContent": "center",
|
"fontSize": 10,
|
||||||
"left": 0,
|
"marginBottom": 1.5,
|
||||||
"opacity": 0,
|
"textAlign": "center",
|
||||||
"position": "absolute",
|
|
||||||
"right": 0,
|
|
||||||
"top": 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
Welcome anonymous
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
|
||||||
accessible={true}
|
|
||||||
allowFontScaling={true}
|
|
||||||
collapsable={undefined}
|
|
||||||
ellipsizeMode="tail"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"color": "rgba(52, 120, 246, 1)",
|
|
||||||
"fontSize": 10,
|
|
||||||
"marginBottom": 1.5,
|
|
||||||
"marginLeft": 0,
|
|
||||||
"marginTop": 0,
|
|
||||||
"textAlign": "center",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Welcome anonymous
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -8,10 +8,12 @@ import {
|
|||||||
View,
|
View,
|
||||||
Platform,
|
Platform,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
|
Dimensions,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import TabBarIcon from './TabBarIcon';
|
import TabBarIcon from './TabBarIcon';
|
||||||
import SafeAreaView from '../SafeAreaView';
|
import SafeAreaView from '../SafeAreaView';
|
||||||
import withOrientation from '../withOrientation';
|
import withOrientation from '../withOrientation';
|
||||||
|
import type { Layout } from 'react-native-tab-view/src/TabViewTypeDefinitions';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
NavigationRoute,
|
NavigationRoute,
|
||||||
@ -51,11 +53,16 @@ type Props = {
|
|||||||
tabStyle?: ViewStyleProp,
|
tabStyle?: ViewStyleProp,
|
||||||
showIcon?: boolean,
|
showIcon?: boolean,
|
||||||
isLandscape: boolean,
|
isLandscape: boolean,
|
||||||
|
layout: Layout,
|
||||||
|
adaptive: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const majorVersion = parseInt(Platform.Version, 10);
|
const majorVersion = parseInt(Platform.Version, 10);
|
||||||
const isIos = Platform.OS === 'ios';
|
const isIos = Platform.OS === 'ios';
|
||||||
const useHorizontalTabs = majorVersion >= 11 && isIos;
|
const isIOS11 = majorVersion >= 11 && isIos;
|
||||||
|
const isTablet =
|
||||||
|
Dimensions.get('window').height / Dimensions.get('window').width < 1.6;
|
||||||
|
const defaultMaxTabBarItemWidth = 125;
|
||||||
|
|
||||||
class TabBarBottom extends React.PureComponent<Props> {
|
class TabBarBottom extends React.PureComponent<Props> {
|
||||||
// See https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/UITabBar.html
|
// See https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/UITabBar.html
|
||||||
@ -67,6 +74,7 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
showLabel: true,
|
showLabel: true,
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
allowFontScaling: true,
|
allowFontScaling: true,
|
||||||
|
adaptive: isIOS11,
|
||||||
};
|
};
|
||||||
|
|
||||||
_renderLabel = (scene: TabScene) => {
|
_renderLabel = (scene: TabScene) => {
|
||||||
@ -99,19 +107,18 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
|
|
||||||
const tintColor = scene.focused ? activeTintColor : inactiveTintColor;
|
const tintColor = scene.focused ? activeTintColor : inactiveTintColor;
|
||||||
const label = this.props.getLabel({ ...scene, tintColor });
|
const label = this.props.getLabel({ ...scene, tintColor });
|
||||||
let marginLeft = 0;
|
|
||||||
if (isLandscape && showIcon && useHorizontalTabs) {
|
|
||||||
marginLeft = LABEL_LEFT_MARGIN;
|
|
||||||
}
|
|
||||||
let marginTop = 0;
|
|
||||||
if (!isLandscape && showIcon && useHorizontalTabs) {
|
|
||||||
marginTop = LABEL_TOP_MARGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof label === 'string') {
|
if (typeof label === 'string') {
|
||||||
return (
|
return (
|
||||||
<Animated.Text
|
<Animated.Text
|
||||||
style={[styles.label, { color, marginLeft, marginTop }, labelStyle]}
|
style={[
|
||||||
|
styles.label,
|
||||||
|
{ color },
|
||||||
|
showIcon && this._shouldUseHorizontalTabs()
|
||||||
|
? styles.labelBeside
|
||||||
|
: styles.labelBeneath,
|
||||||
|
labelStyle,
|
||||||
|
]}
|
||||||
allowFontScaling={allowFontScaling}
|
allowFontScaling={allowFontScaling}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
@ -147,7 +154,7 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
inactiveTintColor={inactiveTintColor}
|
inactiveTintColor={inactiveTintColor}
|
||||||
renderIcon={renderIcon}
|
renderIcon={renderIcon}
|
||||||
scene={scene}
|
scene={scene}
|
||||||
style={showLabel && useHorizontalTabs ? {} : styles.icon}
|
style={showLabel && this._shouldUseHorizontalTabs() ? {} : styles.icon}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -158,6 +165,65 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
return testIDProps;
|
return testIDProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_tabItemMaxWidth() {
|
||||||
|
const { tabStyle, layout } = this.props;
|
||||||
|
let maxTabBarItemWidth;
|
||||||
|
|
||||||
|
const flattenedTabStyle = StyleSheet.flatten(tabStyle);
|
||||||
|
|
||||||
|
if (flattenedTabStyle) {
|
||||||
|
if (typeof flattenedTabStyle.width === 'number') {
|
||||||
|
maxTabBarItemWidth = flattenedTabStyle.width;
|
||||||
|
} else if (
|
||||||
|
typeof flattenedTabStyle.width === 'string' &&
|
||||||
|
flattenedTabStyle.endsWith('%')
|
||||||
|
) {
|
||||||
|
const width = parseFloat(flattenedTabStyle.width);
|
||||||
|
if (Number.isFinite(width)) {
|
||||||
|
maxTabBarItemWidth = layout.width * (width / 100);
|
||||||
|
}
|
||||||
|
} else if (typeof flattenedTabStyle.maxWidth === 'number') {
|
||||||
|
maxTabBarItemWidth = flattenedTabStyle.maxWidth;
|
||||||
|
} else if (
|
||||||
|
typeof flattenedTabStyle.maxWidth === 'string' &&
|
||||||
|
flattenedTabStyle.endsWith('%')
|
||||||
|
) {
|
||||||
|
const width = parseFloat(flattenedTabStyle.maxWidth);
|
||||||
|
if (Number.isFinite(width)) {
|
||||||
|
maxTabBarItemWidth = layout.width * (width / 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!maxTabBarItemWidth) {
|
||||||
|
maxTabBarItemWidth = defaultMaxTabBarItemWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxTabBarItemWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
_shouldUseHorizontalTabs() {
|
||||||
|
const { routes } = this.props.navigation.state;
|
||||||
|
const { isLandscape, layout, adaptive, tabStyle } = this.props;
|
||||||
|
|
||||||
|
if (!adaptive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tabBarWidth = layout.width;
|
||||||
|
if (tabBarWidth === 0) {
|
||||||
|
return isTablet;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHeightConstrained = layout.height < 500;
|
||||||
|
if (isHeightConstrained) {
|
||||||
|
return isLandscape;
|
||||||
|
} else {
|
||||||
|
const maxTabBarItemWidth = this._tabItemMaxWidth();
|
||||||
|
return routes.length * maxTabBarItemWidth <= tabBarWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
position,
|
position,
|
||||||
@ -171,17 +237,20 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
animateStyle,
|
animateStyle,
|
||||||
tabStyle,
|
tabStyle,
|
||||||
isLandscape,
|
isLandscape,
|
||||||
|
layout,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { routes } = navigation.state;
|
const { routes } = navigation.state;
|
||||||
const previousScene = routes[navigation.state.index];
|
const previousScene = routes[navigation.state.index];
|
||||||
// Prepend '-1', so there are always at least 2 items in inputRange
|
// Prepend '-1', so there are always at least 2 items in inputRange
|
||||||
const inputRange = [-1, ...routes.map((x: *, i: number) => i)];
|
const inputRange = [-1, ...routes.map((x: *, i: number) => i)];
|
||||||
|
|
||||||
|
const isHeightConstrained =
|
||||||
|
layout.height === 0 ? !isTablet : layout.height < 500;
|
||||||
const tabBarStyle = [
|
const tabBarStyle = [
|
||||||
styles.tabBar,
|
styles.tabBar,
|
||||||
isLandscape && useHorizontalTabs
|
this._shouldUseHorizontalTabs() && isHeightConstrained
|
||||||
? styles.tabBarLandscape
|
? styles.tabBarCompact
|
||||||
: styles.tabBarPortrait,
|
: styles.tabBarRegular,
|
||||||
style,
|
style,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -220,17 +289,19 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
? onPress({ previousScene, scene, jumpToIndex })
|
? onPress({ previousScene, scene, jumpToIndex })
|
||||||
: jumpToIndex(index)}
|
: jumpToIndex(index)}
|
||||||
>
|
>
|
||||||
<Animated.View
|
<Animated.View style={[styles.tab, { backgroundColor }]}>
|
||||||
style={[
|
<View
|
||||||
styles.tab,
|
style={[
|
||||||
isLandscape && useHorizontalTabs && styles.tabLandscape,
|
styles.tab,
|
||||||
!isLandscape && useHorizontalTabs && styles.tabPortrait,
|
this._shouldUseHorizontalTabs()
|
||||||
{ backgroundColor },
|
? styles.tabLandscape
|
||||||
tabStyle,
|
: styles.tabPortrait,
|
||||||
]}
|
tabStyle,
|
||||||
>
|
]}
|
||||||
{this._renderIcon(scene)}
|
>
|
||||||
{this._renderLabel(scene)}
|
{this._renderIcon(scene)}
|
||||||
|
{this._renderLabel(scene)}
|
||||||
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
);
|
);
|
||||||
@ -241,8 +312,6 @@ class TabBarBottom extends React.PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LABEL_LEFT_MARGIN = 20;
|
|
||||||
const LABEL_TOP_MARGIN = 15;
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
tabBar: {
|
tabBar: {
|
||||||
backgroundColor: '#F7F7F7', // Default background color in iOS 10
|
backgroundColor: '#F7F7F7', // Default background color in iOS 10
|
||||||
@ -250,16 +319,15 @@ const styles = StyleSheet.create({
|
|||||||
borderTopColor: 'rgba(0, 0, 0, .3)',
|
borderTopColor: 'rgba(0, 0, 0, .3)',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
tabBarLandscape: {
|
tabBarCompact: {
|
||||||
height: 29,
|
height: 29,
|
||||||
},
|
},
|
||||||
tabBarPortrait: {
|
tabBarRegular: {
|
||||||
height: 49,
|
height: 49,
|
||||||
},
|
},
|
||||||
tab: {
|
tab: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: isIos ? 'center' : 'stretch',
|
alignItems: isIos ? 'center' : 'stretch',
|
||||||
justifyContent: 'flex-end',
|
|
||||||
},
|
},
|
||||||
tabPortrait: {
|
tabPortrait: {
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
@ -274,9 +342,15 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
labelBeneath: {
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
marginBottom: 1.5,
|
marginBottom: 1.5,
|
||||||
backgroundColor: 'transparent',
|
},
|
||||||
|
labelBeside: {
|
||||||
|
fontSize: 13,
|
||||||
|
marginLeft: 20,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,68 +61,81 @@ exports[`TabBarBottom renders successfully 1`] = `
|
|||||||
"alignItems": "center",
|
"alignItems": "center",
|
||||||
"backgroundColor": "rgba(0, 0, 0, 0)",
|
"backgroundColor": "rgba(0, 0, 0, 0)",
|
||||||
"flex": 1,
|
"flex": 1,
|
||||||
"justifyContent": "flex-end",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testID={undefined}
|
testID={undefined}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"flexGrow": 1,
|
Object {
|
||||||
}
|
"alignItems": "center",
|
||||||
|
"flex": 1,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"flexDirection": "column",
|
||||||
|
"justifyContent": "flex-end",
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
collapsable={undefined}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"flexGrow": 1,
|
||||||
"bottom": 0,
|
|
||||||
"justifyContent": "center",
|
|
||||||
"left": 0,
|
|
||||||
"opacity": 1,
|
|
||||||
"position": "absolute",
|
|
||||||
"right": 0,
|
|
||||||
"top": 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<View
|
<View
|
||||||
|
collapsable={undefined}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"alignItems": "center",
|
||||||
|
"bottom": 0,
|
||||||
|
"justifyContent": "center",
|
||||||
|
"left": 0,
|
||||||
|
"opacity": 1,
|
||||||
|
"position": "absolute",
|
||||||
|
"right": 0,
|
||||||
|
"top": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
collapsable={undefined}
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"alignItems": "center",
|
||||||
|
"bottom": 0,
|
||||||
|
"justifyContent": "center",
|
||||||
|
"left": 0,
|
||||||
|
"opacity": 0,
|
||||||
|
"position": "absolute",
|
||||||
|
"right": 0,
|
||||||
|
"top": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text
|
||||||
|
accessible={true}
|
||||||
|
allowFontScaling={true}
|
||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
|
ellipsizeMode="tail"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"backgroundColor": "transparent",
|
||||||
"bottom": 0,
|
"color": "rgba(52, 120, 246, 1)",
|
||||||
"justifyContent": "center",
|
"fontSize": 10,
|
||||||
"left": 0,
|
"marginBottom": 1.5,
|
||||||
"opacity": 0,
|
"textAlign": "center",
|
||||||
"position": "absolute",
|
|
||||||
"right": 0,
|
|
||||||
"top": 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
s1
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
|
||||||
accessible={true}
|
|
||||||
allowFontScaling={true}
|
|
||||||
collapsable={undefined}
|
|
||||||
ellipsizeMode="tail"
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"color": "rgba(52, 120, 246, 1)",
|
|
||||||
"fontSize": 10,
|
|
||||||
"marginBottom": 1.5,
|
|
||||||
"marginLeft": 0,
|
|
||||||
"marginTop": 0,
|
|
||||||
"textAlign": "center",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
s1
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user