Skip to content

Commit

Permalink
fix(iOS): fullscreenmodal color scheme adaptability (#2211)
Browse files Browse the repository at this point in the history
## Description

This PR fixes the color scheme adaptability when using `presentation:
fullScreenModal`.
When changing the appearance mode between `light` and `dark` the screens
reacted immediately except for the fullScreenModal presentation style.

When using a `UIModalPresentationFullScreen` the views belonging to the
presenting view controller are removed after the presentation completes.
After investigating the view hierarchy for a screen with `presentation:
fullScreenModal` it turned out that the `RCTRootView` is removed, thus
the `traitCollectionDidChange` observer ([see
here](https://github.com/facebook/react-native/blob/d3e0430deac573fd44792e6005d5de20e9ad2797/packages/react-native/React/Base/RCTRootView.m#L362))
is not working anymore.

The solution was to add an extra observer for
`RNSScreenStackPresentationFullScreenModal` to be able to send proper
notifications when no RCTRootView is present. As the observer is working
synchronously it's usage is limited for this presentation style only.

Fixes #2002.


## Changes

- added repro `Test2002.tsx`
- added `traitCollectionDidChange` observer for fullScreenModal


## Screenshots / GIFs

### Before


https://github.com/software-mansion/react-native-screens/assets/91994767/52fa4e92-3baa-49e2-b278-7be57c4d28b3

### After

https://github.com/software-mansion/react-native-screens/assets/91994767/74c62c45-c793-4f63-81fc-d68d4000fea6


## Test code and steps to reproduce

- added `Test2002.tsx` to test examples

## Checklist

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Updated documentation: <!-- For adding new props to native-stack
-->
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [ ] Ensured that CI passes

---------

Co-authored-by: Kacper Kafara <kacperkafara@gmail.com>
Co-authored-by: Kacper Kafara <kacper.kafara@swmansion.com>
  • Loading branch information
3 people authored Jun 26, 2024
1 parent da9b362 commit c460fb7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/test-examples/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import Test1844 from './src/Test1844';
import Test1864 from './src/Test1864';
import Test1970 from './src/Test1970';
import Test1981 from './src/Test1981';
import Test2002 from './src/Test2002';
import Test2008 from './src/Test2008';
import Test2028 from './src/Test2028';
import Test2048 from './src/Test2048';
Expand Down
59 changes: 59 additions & 0 deletions apps/test-examples/src/Test2002.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react';
import { View, Button, useColorScheme, StyleSheet } from 'react-native';
import {
DarkTheme,
DefaultTheme,
NavigationContainer,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Button
onPress={() => navigation.navigate('fullScreenModal')}
title="Open fullScreenModal"
/>
<Button
onPress={() => navigation.navigate('formSheet')}
title="Open formSheet"
/>
</View>
);
}

function ModalScreen({ navigation }) {
return (
<View style={styles.container}>
<Button onPress={() => navigation.goBack()} title="Dismiss" />
</View>
);
}

const RootStack = createNativeStackNavigator();

export default function App() {
const scheme = useColorScheme();

return (
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={HomeScreen} />
<RootStack.Screen
name="formSheet"
component={ModalScreen}
options={{ presentation: 'formSheet' }}
/>
<RootStack.Screen
name="fullScreenModal"
component={ModalScreen}
options={{ presentation: 'fullScreenModal' }}
/>
</RootStack.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
});
22 changes: 22 additions & 0 deletions ios/RNSModalScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@

@implementation RNSModalScreen

// When using UIModalPresentationStyleFullScreen the whole view hierarchy mounted under primary `UITransitionView` is
// removed, including React's root view, which observes for trait collection changes & sends it to `Appearance` module
// via system notification centre. To workaround this detached-root-view-situation we emit the event to React's
// `Appearance` module ourselves. For the RCTRootView observer, visit
// https://github.com/facebook/react-native/blob/d3e0430deac573fd44792e6005d5de20e9ad2797/packages/react-native/React/Base/RCTRootView.m#L362
// For more information, see https://github.com/software-mansion/react-native-screens/pull/2211.
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
[super traitCollectionDidChange:previousTraitCollection];
if (RCTSharedApplication().applicationState == UIApplicationStateBackground ||
self.stackPresentation != RNSScreenStackPresentationFullScreenModal) {
return;
}

[[NSNotificationCenter defaultCenter]
postNotificationName:RCTUserInterfaceStyleDidChangeNotification
object:self
userInfo:@{
RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey : self.traitCollection,
}];
}

#ifdef RCT_NEW_ARCH_ENABLED
+ (react::ComponentDescriptorProvider)componentDescriptorProvider
{
Expand Down

0 comments on commit c460fb7

Please sign in to comment.