Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix (iOS): Swipe down to dismiss screen with ScrollView on iOS #1902

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import Test1791 from './src/Test1791';
import Test1802 from './src/Test1802';
import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1902 from './src/Test1902';

enableFreeze(true);

Expand Down
69 changes: 69 additions & 0 deletions TestsExample/src/Test1902.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react';
import { Button, View, Text, ScrollView } from 'react-native';
import { NavigationContainer, ParamListBase } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NativeStackNavigationProp } from 'react-native-screens/native-stack';

const Stack = createNativeStackNavigator();

type NavProp = {
navigation: NativeStackNavigationProp<ParamListBase>;
};

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="home"
component={Home}
options={{ headerTitle: 'Home' }}
/>
<Stack.Screen
name="screenA"
component={ScreenA}
options={{ headerShown: false, animation: 'slide_from_bottom', fullScreenGestureEnabled: true, gestureDirection: 'vertical' }}
/>
<Stack.Screen
name="screenB"
component={ScreenB}
options={{ headerShown: false, animation: 'slide_from_bottom' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

const Home = ({ navigation }: NavProp) => (
<View style={{ flex: 1, alignItems: 'center', paddingTop: 50 }}>
<Text>Home</Text>
<Button
onPress={() => navigation.navigate('screenA')}
title="Go to dismissable screen"
/>
<Button
onPress={() => navigation.navigate('screenB')}
title="Go to undismissable screen"
/>
</View>
);

const ScreenA = ({ }: NavProp) => (
<ScrollView contentContainerStyle={{ flex: 1 }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Dismissable screen</Text>
</View>
</ScrollView>
);

const ScreenB = ({ navigation }: NavProp) => (
<ScrollView contentContainerStyle={{ flex: 1 }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Undismissable screen</Text>
<Button
onPress={() => navigation.goBack()}
title="Back"
/>
</View>
</ScrollView>
);
14 changes: 14 additions & 0 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,20 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
BOOL isBackGesture = [panGestureRecognizer translationInView:panGestureRecognizer.view].x > 0 &&
_controller.viewControllers.count > 1;

// Detects if we are currently at the top of the ScrollView and if the gesture is a swipe down.
// We consider this only if the scroll is more vertical than horizontal
UIScrollView *scrollView = (UIScrollView *)otherGestureRecognizer.view;
CGPoint translation = [panGestureRecognizer translationInView:panGestureRecognizer.view];
BOOL shouldDismissWithScrollDown =
abs(translation.y) > abs(translation.x) && translation.y > 0 && scrollView.contentOffset.y <= 0;
if (shouldDismissWithScrollDown) {
// If we detect a dismiss swipe, we cancel the other gesture recognizer and consider only the screen's one
// (To cancel a gesture, we need to disable and re-enable it)
[otherGestureRecognizer setEnabled:NO];
[otherGestureRecognizer setEnabled:YES];
return YES;
}

if (gestureRecognizer.state == UIGestureRecognizerStateBegan || isBackGesture) {
return NO;
}
Expand Down
Loading