Skip to content

Commit

Permalink
feat(iOS): add support for medium detent (#1649)
Browse files Browse the repository at this point in the history
## Description


Add support for
[`sheetPresentationController`](https://developer.apple.com/documentation/uikit/uiviewcontroller/3850571-sheetpresentationcontroller?language=objc)
by exposing new props. These changes work on both architectures (Fabric
& Paper).

Known issues:

* Transition animations between detent sizes are not working on Fabric. 

Nevertheless after the review process I'm gonna merge this PR as it is
getting to big and address these issues in separate PRs.

## Changes

* [x] Added `sheetAllowedDetens` prop
   * Describes heights where a sheet can rest.
   * Works only when `stackPresentation` is set to `formSheet`.
   * Defaults to `large`.

* [x] Added `sheetCornerRadius` prop
   * The corner radius that the sheet will try to render with.
   * Works only when `stackPresentation` is set to `formSheet`.
   * If left unset system default is used.

* [x] Added `sheetLargestUndimmedDetent` prop
* The largest sheet detent for which a view underneath won't be dimmed.
   * Works only when `stackPresentation` is set to `formSheet`.
   * If this prop is set to:
   * - `large` - the view underneath won't be dimmed at any detent level
* - `medium` - the view underneath will be dimmed only when detent level
is `large`
   * - `all` - the view underneath will be dimmed for any detent level
   * Defaults to `all`.

* [x] Added `sheetGrabberVisible` prop
   * Boolean indicating whether the sheet shows a grabber at the top.
   * Works only when `stackPresentation` is set to `formSheet`.
   * Defaults to `false`.

* [x] Added `sheetExpandsWhenScrolledToEdge` prop
   * Whether the sheet should expand to larger detent when scrolling.
   * Works only when `stackPresentation` is set to `formSheet`.
   * Defaults to `true`.

* [x] Added noop implementations for Android
* [x] Updated view manager interfaces for Paper

## Screenshots / GIFs

## Test code and steps to reproduce

`Test1649` in `TestsExample` & `FabricTestExample`

## Checklist

- [x] Included code example that can be used to test this change
- [x] Updated TS types
- [x] Updated documentation: <!-- For adding new props to native-stack
-->
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [x] Ensured that CI passes
  • Loading branch information
kkafar authored Dec 14, 2022
1 parent e871092 commit 914730d
Show file tree
Hide file tree
Showing 20 changed files with 797 additions and 6 deletions.
1 change: 1 addition & 0 deletions FabricTestExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import Test1419 from './src/Test1419';
import Test1473 from './src/Test1473';
import Test1476 from './src/Test1476';
import Test1646 from './src/Test1646';
import Test1649 from './src/Test1649';

enableFreeze(true);

Expand Down
4 changes: 2 additions & 2 deletions FabricTestExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ PODS:
- React-Codegen
- React-RCTFabric
- ReactCommon/turbomodule/core
- RNReanimated (3.0.0-rc.8):
- RNReanimated (3.0.0-rc.2):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
Expand Down Expand Up @@ -1007,7 +1007,7 @@ SPEC CHECKSUMS:
React-runtimeexecutor: 7401c4a40f8728fd89df4a56104541b760876117
ReactCommon: c9246996e73bf75a2c6c3ff15f1e16707cdc2da9
RNGestureHandler: 182b4e135cc4fec4988687e2f123e302dc6b4b71
RNReanimated: 5fe97ed1710f9d0c90bd97b275c4ba6b58fa9b45
RNReanimated: a91b2ed2ac39b96a6a024ab55a6b546b1ec10f84
RNScreens: 208223c783496e6d0aa92ffdf307f61d58756fc1
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: eca980a5771bf114c41a754098cd85e6e0d90ed7
Expand Down
196 changes: 196 additions & 0 deletions FabricTestExample/src/Test1649.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/* eslint-disable react/display-name */
import * as React from 'react';
import {Button, StyleSheet, View, Text, ScrollView} from 'react-native';
import {NavigationContainer, ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
NativeStackNavigationOptions
} from 'react-native-screens/native-stack';
import { SheetDetentTypes } from 'react-native-screens';

const Stack = createNativeStackNavigator();

export default function App(): JSX.Element {
const initialScreenOptions: NativeStackNavigationOptions = {
stackPresentation: 'formSheet',
sheetAllowedDetents: 'all',
sheetLargestUndimmedDetent: 'medium',
sheetGrabberVisible: false,
sheetCornerRadius: -1,
sheetExpandsWhenScrolledToEdge: true
}

return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
// headerRight: () => <View style={styles.headerView} />,
headerTitleStyle: {
color: 'cyan',
},
headerShown: true,
headerHideBackButton: false
}}>
<Stack.Screen name="First" component={First} />
<Stack.Screen name="Second" component={Second} options={{
fullScreenSwipeEnabled: true,
}} />
<Stack.Screen name="SheetScreen" component={SheetScreen} options={{
...initialScreenOptions
}}/>
<Stack.Screen name="SheetScreenWithScrollView" component={SheetScreenWithScrollView} options={{
...initialScreenOptions
}}/>

</Stack.Navigator>
</NavigationContainer>
);
}

function First({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<Button
title="Tap me for the second screen"
onPress={() => navigation.navigate('Second')}
/>
);
}

function Second({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<>
<Button
title="Open the sheet"
onPress={() => navigation.navigate("SheetScreen")}
/>
<Button
title="Open the sheet with ScrollView"
onPress={() => navigation.navigate("SheetScreenWithScrollView")}
/>
</>
)
}


function SheetScreen({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
const [radius, setRadius] = React.useState(-1);
const [detent, setDetent] = React.useState<SheetDetentTypes>('all');
const [largestUndimmedDetent, sheetLargestUndimmedDetent] = React.useState<SheetDetentTypes>('all');
const [isGrabberVisible, setIsGrabberVisible] = React.useState(false);
// navigation
const [shouldExpand, setShouldExpand] = React.useState(true)

function nextDetentLevel(currDetent: SheetDetentTypes): SheetDetentTypes {
if (currDetent === "all") {
return "medium";
} else if (currDetent === "medium") {
return "large";
} else if (currDetent === "large") {
return "all";
} else {
console.warn("Unhandled sheetDetent type")
return "all";
}
}

return (
<View style={styles.centeredView}>
<Button
title="Tap me for the first screen"
onPress={() => navigation.navigate('First')} />
<Button
title="Tap me for the second screen"
onPress={() => navigation.navigate('Second')} />
<Button
title="Change the corner radius"
onPress={() => {
const newRadius = radius >= 150 ? -1.0 : radius + 50;
setRadius(newRadius)
navigation.setOptions({
sheetCornerRadius: newRadius
})
}}/>
<Text>radius: {radius}</Text>
<Button
title="Change detent level"
onPress={() => {
const newDetentLevel = nextDetentLevel(detent);
setDetent(newDetentLevel)
navigation.setOptions({
sheetAllowedDetents: newDetentLevel
})
}}
/>
<Text>detent: {detent}</Text>
<Button
title="Change largest undimmed detent"
onPress={() => {
const newDetentLevel = nextDetentLevel(largestUndimmedDetent);
sheetLargestUndimmedDetent(newDetentLevel);
navigation.setOptions({
sheetLargestUndimmedDetent: newDetentLevel
})
}}
/>
<Text>largestUndimmedDetent: {largestUndimmedDetent}</Text>
<Button
title="Toggle sheetExpandsWhenScrolledToEdge"
onPress={() => {
setShouldExpand(!shouldExpand);
navigation.setOptions({
sheetExpandsWhenScrolledToEdge: !shouldExpand
})
}}
/>
<Text>sheetExpandsWhenScrolledToEdge: {shouldExpand ? "true" : "false"}</Text>
<Button
title="Toggle grabber visibility"
onPress={() => {
setIsGrabberVisible(!isGrabberVisible);
navigation.setOptions({
sheetGrabberVisible: !isGrabberVisible
})
}}
/>
</View>
);
}

function SheetScreenWithScrollView({ navigation }: { navigation: NativeStackNavigationProp<ParamListBase> }) {
return (
<>
<View style={styles.centeredView}>
<ScrollView>
<SheetScreen navigation={navigation} />
{[...Array(40).keys()].map((val) => <Text key={`${val}`}>Some component {val}</Text>)}
</ScrollView>
</View>
</>
)
}


const styles = StyleSheet.create({
headerView: {
height: 20,
width: 20,
backgroundColor: 'red',
},
centeredView: {
justifyContent: 'center',
alignItems: 'center'
}
});
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import Test1476 from './src/Test1476';
import Test1509 from './src/Test1509';
import Test1539 from './src/Test1539';
import Test1646 from './src/Test1646';
import Test1649 from './src/Test1649';

enableFreeze(true);

Expand Down
4 changes: 2 additions & 2 deletions TestsExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ PODS:
- FlipperKit/FlipperKitNetworkPlugin
- fmt (6.2.1)
- glog (0.3.5)
- hermes-engine (0.70.0)
- hermes-engine (0.70.5)
- libevent (2.1.12)
- OpenSSL-Universal (1.1.1100)
- RCT-Folly (2021.07.22.00):
Expand Down Expand Up @@ -589,7 +589,7 @@ SPEC CHECKSUMS:
FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: 8e84f1284180801c1a1b241f443ba64f931ff561
hermes-engine: 7fe5fc6ef707b7fdcb161b63898ec500e285653d
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
Expand Down
Loading

0 comments on commit 914730d

Please sign in to comment.