Skip to content

Commit 1217390

Browse files
authored
fix(ios): use RNSLifecycleListenerProtocol for screen dismiss detection (#402)
* chore: add goBack test case on example * fix(ios): create snapshot of containerView during unmount * fix(ios): use RNSLifecycleListenerProtocol for screen dismiss detection - Add RNSLifecycleListenerProtocol to react-native-screens patch - TrueSheetViewController conforms to protocol to receive screen lifecycle events - Rename viewControllerDidDetectPresenterDismiss to viewControllerDidDetectScreenDismiss - Remove notification-based approach in favor of protocol-based approach * docs: update changelog and navigation guide for PR #402
1 parent 7534fa1 commit 1217390

14 files changed

Lines changed: 146 additions & 61 deletions

File tree

‎.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch‎

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,62 @@
11
diff --git a/ios/RNSScreen.mm b/ios/RNSScreen.mm
2+
index 65c18f1ddccc64b3169e050e577eab77fd8c183b..8ffd9d4b48f85e53c6fcf0a76963cfcf946dad97 100644
23
--- a/ios/RNSScreen.mm
34
+++ b/ios/RNSScreen.mm
4-
@@ -605,6 +605,11 @@
5-
- (void)notifyWillDisappear
6-
{
5+
@@ -38,6 +38,7 @@
6+
#import "RNSTabBarController.h"
7+
8+
#import "RNSDefines.h"
9+
+#import "integrations/RNSLifecycleListenerProtocol.h"
10+
#import "UIView+RNSUtility.h"
11+
12+
#ifdef RCT_NEW_ARCH_ENABLED
13+
@@ -74,6 +75,7 @@ struct ContentWrapperBox {
14+
ContentWrapperBox _contentWrapperBox;
15+
bool _sheetHasInitialDetentSet;
16+
BOOL _shouldUpdateScrollEdgeEffects;
17+
+ RNSScreen *_controllerBeforeInvalidate;
18+
#ifdef RCT_NEW_ARCH_ENABLED
19+
RCTSurfaceTouchHandler *_touchHandler;
20+
react::RNSScreenShadowNode::ConcreteState::Shared _state;
21+
@@ -608,6 +610,26 @@ RNS_IGNORE_SUPER_CALL_END
722
if (_hideKeyboardOnSwipe) {
823
[self endEditing:YES];
924
}
1025
+
11-
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RNSScreenViewWillDisappear"
12-
+ object:self
13-
+ userInfo:nil];
26+
+ // Notify any presented view controllers that conform to RNSLifecycleListenerProtocol
27+
+ RNSScreen *controller = _controller ?: _controllerBeforeInvalidate;
28+
+ if (controller) {
29+
+ UIViewController *presented = controller.presentedViewController;
30+
+ while (presented) {
31+
+ UIViewController *next = presented.presentedViewController;
32+
+ if ([presented conformsToProtocol:@protocol(RNSLifecycleListenerProtocol)]) {
33+
+ BOOL isPresenterUnmounting = NO;
34+
+ RNSScreen *presenter = (RNSScreen *)presented.presentingViewController;
35+
+ if ([presenter isKindOfClass:[RNSScreen class]]) {
36+
+ isPresenterUnmounting = presenter.screenView.isMarkedForUnmountInCurrentTransaction;
37+
+ }
38+
+ [(id<RNSLifecycleListenerProtocol>)presented screenWillDisappear:controller
39+
+ isPresenterUnmounting:isPresenterUnmounting];
40+
+ }
41+
+ presented = next;
42+
+ }
43+
+ }
1444
+
1545
#ifdef RCT_NEW_ARCH_ENABLED
46+
// If screen is already unmounted then there will be no event emitter
47+
if (_eventEmitter != nullptr) {
48+
@@ -927,6 +949,9 @@ RNS_IGNORE_SUPER_CALL_END
49+
50+
- (void)invalidate
51+
{
52+
+ if (_controller && !_controllerBeforeInvalidate) {
53+
+ _controllerBeforeInvalidate = _controller;
54+
+ }
55+
_controller = nil;
56+
[_sheetsScrollView removeObserver:self forKeyPath:@"bounds" context:nil];
57+
}
1658
diff --git a/ios/RNSScreenStack.mm b/ios/RNSScreenStack.mm
59+
index 51f021831aed26a4eed3c85014020423b7b3108b..268fa69dfee2b20d8b5a66c77c1b4cbd8c831573 100644
1760
--- a/ios/RNSScreenStack.mm
1861
+++ b/ios/RNSScreenStack.mm
1962
@@ -640,8 +640,10 @@ RNS_IGNORE_SUPER_CALL_END
@@ -64,6 +107,7 @@ diff --git a/ios/RNSScreenStack.mm b/ios/RNSScreenStack.mm
64107

65108
// We didn't detect any controllers for dismissal, thus we start presenting new VCs
66109
diff --git a/ios/integrations/RNSDismissibleModalProtocol.h b/ios/integrations/RNSDismissibleModalProtocol.h
110+
index 006f809d104c1d4fbdf6eccca89d6c6e190cca71..89e297f1b7a9582fee3e19237dfba8d4c87a352f 100644
67111
--- a/ios/integrations/RNSDismissibleModalProtocol.h
68112
+++ b/ios/integrations/RNSDismissibleModalProtocol.h
69113
@@ -1,3 +1,5 @@
@@ -86,3 +130,23 @@ diff --git a/ios/integrations/RNSDismissibleModalProtocol.h b/ios/integrations/R
86130
@end
87131

88132
NS_ASSUME_NONNULL_END
133+
diff --git a/ios/integrations/RNSLifecycleListenerProtocol.h b/ios/integrations/RNSLifecycleListenerProtocol.h
134+
new file mode 100644
135+
index 0000000000000000000000000000000000000000..025b4231c0b45f9f10034280037617b9b6d6fec4
136+
--- /dev/null
137+
+++ b/ios/integrations/RNSLifecycleListenerProtocol.h
138+
@@ -0,0 +1,14 @@
139+
+#import <UIKit/UIKit.h>
140+
+
141+
+NS_ASSUME_NONNULL_BEGIN
142+
+
143+
+@protocol RNSLifecycleListenerProtocol <NSObject>
144+
+
145+
+// Called when a screen in the presenting hierarchy is about to disappear.
146+
+// @param screen The screen controller that is disappearing
147+
+// @param isPresenterUnmounting YES if the presenter (modal) itself is being unmounted
148+
+- (void)screenWillDisappear:(UIViewController *)screen isPresenterUnmounting:(BOOL)isPresenterUnmounting;
149+
+
150+
+@end
151+
+
152+
+NS_ASSUME_NONNULL_END

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
### 🐛 Bug fixes
1010

11-
- Fixed sheet not dismissing when presenter screen is popped from navigation. ([#400](https://github.com/lodev09/react-native-true-sheet/pull/400) by [@lodev09](https://github.com/lodev09))
11+
- Fixed sheet not dismissing when presenter screen is popped from navigation. ([#400](https://github.com/lodev09/react-native-true-sheet/pull/400), [#402](https://github.com/lodev09/react-native-true-sheet/pull/402) by [@lodev09](https://github.com/lodev09))
1212
- **iOS**: Fixed position tracking for pending detent changes. ([#394](https://github.com/lodev09/react-native-true-sheet/pull/394) by [@lodev09](https://github.com/lodev09))
1313
- **Android**: Fixed keyboard and focus handling inside RN Modal. ([#387](https://github.com/lodev09/react-native-true-sheet/pull/387) by [@lodev09](https://github.com/lodev09))
1414

‎android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
129129
if (child is TrueSheetContainerView) {
130130
child.delegate = null
131131

132-
// Dismiss the sheet when container is removed
132+
// Dismiss when container is removed
133133
if (viewController.isPresented) {
134-
viewController.dismiss(animated = false)
134+
dismissAll(false) {}
135135
}
136136
}
137137
viewController.removeView(child)
@@ -476,7 +476,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
476476
eventDispatcher?.dispatchEvent(BackPressEvent(surfaceId, id))
477477
}
478478

479-
override fun viewControllerDidDetectPresenterDismiss() {
479+
override fun viewControllerDidDetectScreenDismiss() {
480480
dismissAll(animated = true) {}
481481
}
482482

‎android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ interface TrueSheetViewControllerDelegate {
6262
fun viewControllerWillBlur()
6363
fun viewControllerDidBlur()
6464
fun viewControllerDidBackPress()
65-
fun viewControllerDidDetectPresenterDismiss()
65+
fun viewControllerDidDetectScreenDismiss()
6666
}
6767

6868
// =============================================================================
@@ -540,7 +540,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
540540
onNonModalScreenPushed = {
541541
// Only handle on root sheet (no parent) to trigger dismissAll
542542
if (isPresented && isSheetVisible && parentSheetView == null) {
543-
delegate?.viewControllerDidDetectPresenterDismiss()
543+
delegate?.viewControllerDidDetectScreenDismiss()
544544
}
545545
}
546546
)

‎docs/docs/guides/navigation.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ export default function SheetLayout() {
204204
## Navigating from Sheets
205205

206206
:::note
207-
Requires a [patch to react-native-screens](https://github.com/lodev09/react-native-true-sheet/blob/main/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch). See [PR #3415](https://github.com/software-mansion/react-native-screens/pull/3415) and [PR #3525](https://github.com/software-mansion/react-native-screens/pull/3525).
207+
Requires a [patch to react-native-screens](https://github.com/lodev09/react-native-true-sheet/blob/main/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch). See [PR #3415](https://github.com/software-mansion/react-native-screens/pull/3415), [PR #3526](https://github.com/software-mansion/react-native-screens/pull/3526).
208208
:::
209209

210210
Navigate directly from sheets - they remain visible when presenting modals on top.

‎example/bare/ios/Podfile.lock‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2646,7 +2646,7 @@ PODS:
26462646
- ReactCommon/turbomodule/core
26472647
- SocketRocket
26482648
- Yoga
2649-
- RNTrueSheet (3.7.0-beta.3):
2649+
- RNTrueSheet (3.7.0):
26502650
- boost
26512651
- DoubleConversion
26522652
- fast_float
@@ -3095,7 +3095,7 @@ SPEC CHECKSUMS:
30953095
RNGestureHandler: e1cf8ef3f11045536eed6bd4f132b003ef5f9a5f
30963096
RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee
30973097
RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479
3098-
RNTrueSheet: 192e3d4a0e32be2f16a06d1f15e939b6e045af2d
3098+
RNTrueSheet: 98de027f434047a746fdf797927f2cfeaf84fc84
30993099
RNWorklets: d9c050940f140af5d8b611d937eab1cbfce5e9a5
31003100
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
31013101
Yoga: 689c8e04277f3ad631e60fe2a08e41d411daf8eb

‎example/bare/src/navigators/ModalStackNavigator.tsx‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ const ModalScreenWrapper = () => {
1818
);
1919
};
2020

21+
const TestScreenWrapper = () => {
22+
const navigation = useNavigation<NativeStackNavigationProp<ModalStackParamList>>();
23+
return <TestScreen onGoBack={() => navigation.goBack()} />;
24+
};
25+
2126
export const ModalStackNavigator = () => {
2227
return (
2328
<ModalStack.Navigator
@@ -27,7 +32,7 @@ export const ModalStackNavigator = () => {
2732
}}
2833
>
2934
<ModalStack.Screen name="Modal" component={ModalScreenWrapper} />
30-
<ModalStack.Screen name="Test" component={TestScreen} />
35+
<ModalStack.Screen name="Test" component={TestScreenWrapper} />
3136
</ModalStack.Navigator>
3237
);
3338
};

‎example/bare/src/navigators/RootNavigator.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createNativeStackNavigator } from '@react-navigation/native-stack';
22

3-
import { MapScreen, StandardScreen, TestScreen } from '@example/shared/screens';
3+
import { MapScreen, StandardScreen } from '@example/shared/screens';
44
import { Map } from '@example/shared/components';
55
import { ModalStackNavigator } from './ModalStackNavigator';
66
import { SheetNavigator } from './SheetNavigator';
@@ -50,7 +50,6 @@ export const RootNavigator = () => {
5050
name="Standard"
5151
component={StandardScreenWrapper}
5252
/>
53-
<Stack.Screen name="Test" component={TestScreen} />
5453
<Stack.Screen
5554
name="ModalStack"
5655
component={ModalStackNavigator}

‎example/expo/app/modal/test.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { TestScreen } from '@example/shared/screens';
2+
import { useRouter } from 'expo-router';
23

34
export default function Test() {
4-
return <TestScreen />;
5+
const router = useRouter();
6+
return <TestScreen onGoBack={() => router.back()} />;
57
}

‎example/shared/src/screens/TestScreen.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ import { BLUE, GAP, SPACING } from '../utils';
66
import { Button } from '../components';
77
import { BasicSheet, PromptSheet, FlatListSheet } from '../components/sheets';
88

9-
export const TestScreen = () => {
9+
interface TestScreenProps {
10+
onGoBack: () => void;
11+
}
12+
13+
export const TestScreen = ({ onGoBack }: TestScreenProps) => {
1014
const basicSheet = useRef<TrueSheet>(null);
1115
const promptSheet = useRef<TrueSheet>(null);
1216
const flatListSheet = useRef<TrueSheet>(null);
1317

1418
return (
1519
<TrueSheetProvider>
1620
<View style={styles.content}>
21+
<Button text="Back Sheet" onPress={onGoBack} />
1722
<Button text="Basic Sheet" onPress={() => basicSheet.current?.present()} />
1823
<Button text="Prompt Sheet" onPress={() => promptSheet.current?.present()} />
1924
<Button text="FlatList Sheet" onPress={() => flatListSheet.current?.present()} />
@@ -29,7 +34,6 @@ export const TestScreen = () => {
2934
const styles = StyleSheet.create({
3035
content: {
3136
backgroundColor: BLUE,
32-
justifyContent: 'center',
3337
flex: 1,
3438
padding: SPACING,
3539
gap: GAP,

0 commit comments

Comments
 (0)