反应导航 ios 模态:可以将模态拖动到比开始时更高的位置



我使用 react-navigation> 5 并尝试配置 iOS 类型的模态。他们在文档中显示的那个有效 好吧,但我希望模态出现在一定高度(如屏幕的三分之一(并停止,并让用户有可能将其拖得更高(从顶部留出一定的余量(或通过向下滑动来禁用模态。 我检查了ModalPresentationIOS中使用的所有配置,但我仍然难以获得正确的行为。目前,我只能设置正确的高度,但用户无法将模态拖得更高。这是我的配置:

<RootStack.Navigator
headerMode="none"
mode="modal"
screenOptions={({ route, navigation }) => ({
cardStyle: { backgroundColor: 'transparent' },
cardOverlayEnabled: true,
gestureEnabled: true,
headerStatusBarHeight:
navigation.dangerouslyGetState().routes.indexOf(route) > 0
? 0
: undefined,
gestureDirection: 'vertical',
transitionSpec: {
open: TransitionIOSSpec,
close: TransitionIOSSpec,
},
cardStyleInterpolator: forModalPresentationIOS,
headerStyleInterpolator: forFade
})}
>

使用此配置进行forModalPresentationIOS

function forModalPresentationIOS({
index,
current,
next,
inverted,
layouts: { screen },
insets,
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
const isLandscape = screen.width > screen.height;
const topOffset = isLandscape ? 0 : 10;
const statusBarHeight = insets.top;
const aspectRatio = screen.height / screen.width;
const topMargin = 100; // Variable that I use to set up the height of the modal
const progress = add(
current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
}),
next
? next.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
})
: 0
);
const translateY = multiply(
progress.interpolate({
inputRange: [0, 1, 2],
outputRange: [
screen.height,
index === 0 ? 0 : topMargin,
(index === 0 ? statusBarHeight : 0) - topOffset * aspectRatio,
],
}),
inverted
);
const overlayOpacity = progress.interpolate({
inputRange: [0, 1, 1.0001, 2],
outputRange: [0, 0.3, 1, 1],
});
const scale = isLandscape
? 1
: progress.interpolate({
inputRange: [0, 1, 2],
outputRange: [
1,
1,
screen.width ? 1 - (topOffset * 2) / screen.width : 1,
],
});
const borderRadius = isLandscape
? 0
: index === 0
? progress.interpolate({
inputRange: [0, 1, 1.0001, 2],
outputRange: [0, 0, isIphoneX() ? 38 : 0, 10],
})
: 10;
return {
cardStyle: {
overflow: 'hidden',
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
marginTop: index === 0 ? 0 : statusBarHeight,
marginBottom: index === 0 ? 0 : topOffset,
transform: [{ translateY }, { scale }],
},
overlayStyle: { opacity: overlayOpacity },
};
}

谢谢!

没有找到任何正确的配置,所以我提供了这个有效的解决方案。我们只是检查用户何时按下模态的标题并移动它,并调整模态的边距顶部以使其移动。这里的代码:

根堆栈

import React, { useState, useEffect } from 'react';
import { Dimensions } from 'react-native';
import {
createStackNavigator,
} from '@react-navigation/stack';
// import components
import MainNav from './MainNav';
import ModalScreen from '../screens/Modal/ModalScreen';
// Types
export type RootStackParamList = {
Main: undefined;
ModalScreen: { handleModalHeight: (arg1: number) => void };
};
interface Props {};
// ***
const { height } = Dimensions.get('window');
const RootStack = createStackNavigator<RootStackParamList>();
const RootNav: React.FunctionComponent<Props> = props => {
function handleModalHeight(modalHeight: number): void {
if(modalHeight >= Math.floor(height/3) && modalHeight <= Math.floor(height -(height/3))){
setMarginTop(modalHeight);
}
}
const [ marginTop, setMarginTop ] = useState<number>(Math.floor(height -(height/3)));
return(
<RootStack.Navigator
mode="modal"
headerMode="none"
>
<RootStack.Screen
name="Main"
component={MainNav}
/>
<RootStack.Screen
name="ModalScreen"
component={ModalScreen}
initialParams={{handleModalHeight}}
options={{
cardStyle: {
overflow: 'hidden',
borderTopLeftRadius: 38,
borderTopRightRadius: 38,
marginTop
},
gestureResponseDistance: {
vertical: marginTop
}
}}
/>
</RootStack.Navigator>
);
}
export default RootNav;

模态屏幕

import React, { useState, useRef, useEffect } from 'react';
import {
Text,
PanResponder,
YellowBox,
TouchableWithoutFeedback,
PanResponderInstance
} from 'react-native';
import styled from 'styled-components/native';
import { RouteProp } from '@react-navigation/native';
YellowBox.ignoreWarnings([
'Non-serializable values were found in the navigation state',
]);
import { RootStackParamList } from '../../navigation/RootNav';
type ModalScreenRouteProps = RouteProp<RootStackParamList, 'ModalScreen'>;
type Props = {
route: ModalScreenRouteProps;
};
const ModalScreen: React.FunctionComponent<Props> = ({route}) => {
const handleModalHeight = route.params?.handleModalHeight;
const [ isModalSpreading, setIsModalSpreading ] = useState<boolean>(false);
const panResponder = useRef<PanResponderInstance | null>(null);
useEffect(()=> {
panResponder.current = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderMove: (evt, gestureState) => {
const { moveY } = gestureState;
if(isModalSpreading) {
handleModalHeight(moveY);
}
},
onPanResponderRelease: () => {
setIsModalSpreading(false)
}
})
}, [isModalSpreading]);
return (
<Container {...panResponder.current?.panHandlers} >
<TouchableWithoutFeedback
onPressIn={() => setIsModalSpreading(true)}
onPressOut={() => setIsModalSpreading(false)}
>
<Header>
<BarHeader/>
</Header>
</TouchableWithoutFeedback>
<BodyModal>
<Text>ModalPlace</Text>
</BodyModal>
</Container>
);
}
const Container = styled.View`
flex: 1;
justify-content: flex-start;
align-items: center;
background-color: white;
`;
const Header = styled.View`
width: 100%;
height: 40px;
justify-content: center;
align-items: center;
`;
const BarHeader = styled.View`
width: 60px;
height: 5px;
background-color: lightgrey;
border-radius: 2.5px;
`;
const BodyModal = styled.View`
justify-content: center;
align-items: center;
background-color: white;
`;
export default ModalScreen;

这种方法很粗糙,性能很差。我认为最好的是过于开放具有透明背景的经典模态,并在用户向上拖动模态时使用 Animated.View 来对视图的高度进行动画处理。

最新更新