Skip to content

Commit

Permalink
feat: change modal logic (#798)
Browse files Browse the repository at this point in the history
PR introducing 3 changes.

- disabled the ability to modify the status bar appearance in non-fullScreen modals since it destroys the animation (see gifs in #767) and introduces the necessity to control the color of status bar in every modal, which does not work well e.g. with phones with iOS <13 since there are only fullScreen modals there. The system seems to pretty well deal with all the cases where this change could be problematic, so I think it is worth to do this.
- extended an if clause in RNSScreenStackHeaderConfig.m to fix the bug when trying to change options via navigation.setOptions while being in fullScreenModal. Till now, when the code reached the changed line, if did not go into if due to nav being nil for fullScreenModal, which resolved to nextVC also being nil. We now check if nav is nil, which should mean that we are in fullScreenModal, and if vc is not nil at the same time, we want to update the screen.
- Removed running tsc --noEmit in TestsExample project since it made the CI fail for some reason with .tsx files.
  • Loading branch information
WoLewicki authored Feb 10, 2021
1 parent 6f6ea86 commit e9d28f1
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 39 deletions.
4 changes: 2 additions & 2 deletions TestsExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ PODS:
- React
- RNReanimated (1.13.1):
- React
- RNScreens (2.17.0):
- RNScreens (2.17.1):
- React-Core
- RNSearchBar (3.5.1):
- React
Expand Down Expand Up @@ -506,7 +506,7 @@ SPEC CHECKSUMS:
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
RNReanimated: dd8c286ab5dd4ba36d3a7fef8bff7e08711b5476
RNScreens: 23c1fbf7797261321f6db93fdcbd9913573535e4
RNScreens: b6c9607e6fe47c1b6e2f1910d2acd46dd7ecea3a
RNSearchBar: 9860431356b7d12a8449d2fddb2b5f3c78d1e99f
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
Expand Down
4 changes: 3 additions & 1 deletion TestsExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest"
"test": "jest",
"lint": "eslint --ext '.js,.ts,.tsx' --fix src && yarn check-types",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@react-native-community/masked-view": "^0.1.10",
Expand Down
61 changes: 40 additions & 21 deletions TestsExample/src/Test642.js → TestsExample/src/Test642.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
// connected PRs: #679, #675
import {NavigationContainer} from '@react-navigation/native';
import React from 'react';
import {ScrollView, StyleSheet, View, Button} from 'react-native';
import {createNativeStackNavigator} from 'react-native-screens/native-stack';
import {NavigationContainer, ParamListBase} from '@react-navigation/native';
import {ScrollView, View, Button} from 'react-native';
import {createNativeStackNavigator, NativeStackNavigationProp} from 'react-native-screens/native-stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

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

const Stack = createNativeStackNavigator();

export default function NativeNavigation() {
export default function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
stackPresentation: 'modal',
stackPresentation: "modal",
}}>
<Stack.Screen
name="Home"
component={Home}
options={{
statusBarStyle: 'light',
statusBarStyle: 'dark',
}}
/>
<Stack.Screen
Expand All @@ -28,15 +32,23 @@ export default function NativeNavigation() {
statusBarStyle: 'dark',
}}
/>
<Stack.Screen
name="Home2"
component={Home}
options={{
statusBarStyle: 'light',
stackPresentation: "fullScreenModal",
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

const Tab = createBottomTabNavigator();

const TabNavigator = (props) => (
<Tab.Navigator screensEnabled={true}>
const TabNavigator = () => (
<Tab.Navigator>
<Tab.Screen name="Tab1" component={Home} />
<Tab.Screen name="Tab2" component={Inner} />
<Tab.Screen name="Tab3" component={Home} />
Expand All @@ -45,7 +57,7 @@ const TabNavigator = (props) => (

const InnerStack = createNativeStackNavigator();

const Inner = (props) => (
const Inner = () => (
<InnerStack.Navigator
screenOptions={{
statusBarStyle: 'dark',
Expand All @@ -54,25 +66,40 @@ const Inner = (props) => (
</InnerStack.Navigator>
);

function Home({navigation}) {
function Home({navigation}: Props) {
const [yes, setYes] = React.useState(true);
return (
<ScrollView
style={{backgroundColor: 'yellow'}}
style={{backgroundColor: 'rgba(255,255,0,0.5)'}}
contentInsetAdjustmentBehavior="automatic"
>
<View style={styles.leftTop} />
<View />
<Button
title="TabNavigator"
onPress={() => {
navigation.push('TabNavigator');
}}
/>
<Button
title="Home2"
onPress={() => {
navigation.push('Home2');
}}
/>
<Button
title="status bar style"
onPress={() => {
navigation.setOptions({
statusBarStyle: Math.random() > 0.5 ? 'light' : 'dark',
statusBarStyle: yes ? 'light' : 'dark',
});
setYes(!yes);
}}
/>
<Button
title="Change title"
onPress={() => {
navigation.setOptions({
title: yes ? 'Home' : 'NotHome',
});
setYes(!yes);
}}
Expand All @@ -86,11 +113,3 @@ function Home({navigation}) {
</ScrollView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
5 changes: 5 additions & 0 deletions TestsExample/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../tsconfig.json",
"baseUrl": ".",
"include": ["**/*.ts", "**/*.tsx", "**/*.js"]
}
23 changes: 12 additions & 11 deletions ios/RNSScreen.m
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ - (instancetype)initWithView:(UIView *)view
#if !TARGET_OS_TV
- (UIViewController *)childViewControllerForStatusBarStyle
{
UIViewController *vc = [self findChildVCForConfig];
UIViewController *vc = [self findChildVCForConfigIncludingModals:NO];
return vc == self ? nil : vc;
}

Expand All @@ -322,7 +322,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle

- (UIViewController *)childViewControllerForStatusBarHidden
{
UIViewController *vc = [self findChildVCForConfig];
UIViewController *vc = [self findChildVCForConfigIncludingModals:NO];
return vc == self ? nil : vc;
}

Expand All @@ -334,7 +334,7 @@ - (BOOL)prefersStatusBarHidden

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
UIViewController *vc = [self findChildVCForConfig];
UIViewController *vc = [self findChildVCForConfigIncludingModals:NO];

if ([vc isKindOfClass:[RNSScreen class]]) {
RNSScreenStackHeaderConfig *config = [(RNSScreen *)vc findScreenConfig];
Expand All @@ -345,7 +345,7 @@ - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
UIViewController *vc = [self findChildVCForConfig];
UIViewController *vc = [self findChildVCForConfigIncludingModals:YES];

if ([vc isKindOfClass:[RNSScreen class]]) {
RNSScreenStackHeaderConfig *config = [(RNSScreen *)vc findScreenConfig];
Expand All @@ -356,19 +356,20 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations

// if the returned vc is a child, it means that it can provide config;
// if the returned vc is self, it means that there is no child for config and self has config to provide,
// so we return self which results in asking self for preferredStatusBarStyle;
// so we return self which results in asking self for preferredStatusBarStyle/Animation etc.;
// if the returned vc is nil, it means none of children could provide config and self does not have config either,
// so if it was asked by parent, it will fallback to parent's option, or use default option if it is the top Screen
- (UIViewController *)findChildVCForConfig
- (UIViewController *)findChildVCForConfigIncludingModals:(BOOL)includingModals
{
UIViewController *lastViewController = [[self childViewControllers] lastObject];
if ([self.presentedViewController isKindOfClass:[RNSScreen class]]) {
lastViewController = self.presentedViewController;
// setting this makes the modal vc being asked for appearance,
// so it doesn't matter what we return here since the modal's root screen will be asked
lastViewController.modalPresentationCapturesStatusBarAppearance = YES;
// we don't want to allow controlling of status bar appearance when we present non-fullScreen modal
// and it is not possible if `modalPresentationCapturesStatusBarAppearance` is not set to YES, so even
// if we went into a modal here and ask it, it wouldn't take any effect. For fullScreen modals, the system
// asks them by itself, so we can stop traversing here.
// for screen orientation, we need to start the search again from that modal
return [(RNSScreen *)lastViewController findChildVCForConfig] ?: lastViewController;
return !includingModals ? nil : [(RNSScreen *)lastViewController findChildVCForConfigIncludingModals:includingModals] ?: lastViewController;
}

UIViewController *selfOrNil = [self findScreenConfig] != nil ? self : nil;
Expand All @@ -382,7 +383,7 @@ - (UIViewController *)findChildVCForConfig
// we use `childViewControllerForStatusBarStyle` for all options since the behavior is the same for all of them
UIViewController *childScreen = [lastViewController childViewControllerForStatusBarStyle];
if ([childScreen isKindOfClass:[RNSScreen class]]) {
return [(RNSScreen *)childScreen findChildVCForConfig] ?: selfOrNil;
return [(RNSScreen *)childScreen findChildVCForConfigIncludingModals:includingModals] ?: selfOrNil;
} else {
return selfOrNil;
}
Expand Down
3 changes: 2 additions & 1 deletion ios/RNSScreenStackHeaderConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ - (void)updateViewControllerIfNeeded
nextVC = nav.topViewController;
}

if (vc != nil && nextVC == vc) {
// if nav is nil, it means we are in a fullScreen modal, so there is no nextVC, but we still want to update
if (vc != nil && (nav == nil || nextVC == vc)) {
[RNSScreenStackHeaderConfig updateViewController:self.screenView.controller
withConfig:self
animated:YES];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"start": "react-native start",
"test:unit": "jest --passWithNoTests",
"format": "prettier --write --list-different './src/**/*.{js,ts,tsx}'",
"lint": "eslint --ext '.js,.ts,.tsx' --fix src && npm run check-types",
"lint": "eslint --ext '.js,.ts,.tsx' --fix src && yarn check-types",
"precommit": "yarn format && yarn lint",
"release": "npm login && release-it",
"prepare": "bob build"
Expand Down
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@
"strict": true,
"target": "esnext"
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx"]
"include": ["src/**/*.ts", "src/**/*.tsx"]
}

0 comments on commit e9d28f1

Please sign in to comment.