Skip to content

Commit 31716d5

Browse files
committed
fix(ios): distinguish modal vs nested stack for auto-dismiss
1 parent 6962e1a commit 31716d5

1 file changed

Lines changed: 50 additions & 14 deletions

File tree

ios/core/RNScreensEventObserver.mm

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,21 @@
1919
@implementation RNScreensEventObserver {
2020
std::weak_ptr<const EventDispatcher> _eventDispatcher;
2121
std::shared_ptr<const EventListener> _eventListener;
22+
NSInteger _presenterScreenTag;
2223
NSMutableSet<NSNumber *> *_screenTags;
2324
__weak UIViewController *_presenterScreenController;
25+
__weak UIViewController *_parentScreenController;
26+
__weak UIWindow *_window;
2427
BOOL _dismissedByNavigation;
2528
}
2629

2730
- (instancetype)init {
2831
if (self = [super init]) {
32+
_presenterScreenTag = 0;
2933
_screenTags = [NSMutableSet new];
3034
_presenterScreenController = nil;
35+
_parentScreenController = nil;
36+
_window = nil;
3137
}
3238
return self;
3339
}
@@ -56,9 +62,11 @@ - (void)startObservingWithState:(const TrueSheetViewState &)state {
5662
Tag screenTag = family->getTag();
5763

5864
if (event.type == "topWillDisappear") {
59-
if ([strongSelf shouldDismissForScreenTag:screenTag]) {
60-
strongSelf->_dismissedByNavigation = YES;
61-
[strongSelf.delegate presenterScreenWillDisappear];
65+
if ([strongSelf->_screenTags containsObject:@(screenTag)]) {
66+
if ([strongSelf shouldDismissForScreenTag:screenTag]) {
67+
strongSelf->_dismissedByNavigation = YES;
68+
[strongSelf.delegate presenterScreenWillDisappear];
69+
}
6270
}
6371
} else if (event.type == "topWillAppear") {
6472
if ([strongSelf->_screenTags containsObject:@(screenTag)] && strongSelf->_dismissedByNavigation) {
@@ -85,40 +93,68 @@ - (void)stopObserving {
8593
}
8694

8795
- (void)capturePresenterScreenFromView:(UIView *)view {
96+
_presenterScreenTag = 0;
8897
[_screenTags removeAllObjects];
8998
_presenterScreenController = nil;
99+
_parentScreenController = nil;
100+
_window = view.window;
90101

91102
for (UIView *current = view.superview; current; current = current.superview) {
92103
if ([NSStringFromClass([current class]) isEqualToString:@"RNSScreenView"]) {
93104
[_screenTags addObject:@(current.tag)];
94105

95-
// Capture the view controller from the first (immediate presenter) screen
96-
if (!_presenterScreenController) {
97-
for (UIResponder *r = current.nextResponder; r; r = r.nextResponder) {
98-
if ([r isKindOfClass:[UIViewController class]]) {
99-
_presenterScreenController = (UIViewController *)r;
100-
break;
101-
}
106+
UIViewController *screenVC = nil;
107+
for (UIResponder *r = current.nextResponder; r; r = r.nextResponder) {
108+
if ([r isKindOfClass:[UIViewController class]]) {
109+
screenVC = (UIViewController *)r;
110+
break;
102111
}
103112
}
113+
114+
if (!_presenterScreenController) {
115+
_presenterScreenTag = current.tag;
116+
_presenterScreenController = screenVC;
117+
} else if (!_parentScreenController && screenVC) {
118+
_parentScreenController = screenVC;
119+
}
104120
}
105121
}
106122
}
107123

108124
- (BOOL)shouldDismissForScreenTag:(NSInteger)screenTag {
109-
if (![_screenTags containsObject:@(screenTag)]) {
125+
// For parent screens (not immediate presenter), check if the presenter screen is being removed
126+
// This handles nested stack removal case
127+
if (screenTag != _presenterScreenTag) {
128+
UINavigationController *parentNav = _parentScreenController.navigationController;
129+
130+
// Modal case: parent's nav is presented -> let sheet dismiss naturally
131+
// Nested stack case: parent's nav is not presented (embedded) -> need to dismiss
132+
if (parentNav.presentingViewController != nil) {
133+
return NO;
134+
}
135+
136+
if (!_parentScreenController) {
137+
return NO;
138+
}
139+
140+
// If presenter view is no longer in window, the nested stack is being removed
141+
UIView *presenterView = [_window viewWithTag:_presenterScreenTag];
142+
if (presenterView == nil || presenterView.window == nil) {
143+
return YES;
144+
}
145+
110146
return NO;
111147
}
112148

149+
// For immediate presenter screen
113150
UINavigationController *navController = _presenterScreenController.navigationController;
114151

115-
// If nav controller is nil or being dismissed, dismiss the sheet
116152
if (!navController || navController.isBeingDismissed) {
117153
return YES;
118154
}
119155

120-
// Skip if screen is still top of nav stack (e.g. modal dismiss - sheet dismisses naturally with modal)
121-
// Dismiss if a new screen was pushed or popped
156+
// Dismiss if presenter is no longer top of nav stack (pushed/popped)
157+
// Skip if still top (e.g. modal dismiss - sheet dismisses naturally)
122158
return navController.topViewController != _presenterScreenController;
123159
}
124160

0 commit comments

Comments
 (0)