Skip to content

Commit 94977b9

Browse files
committed
Fix modal and alert dismissal bugs on iOS [v2] (facebook#22237)
1 parent 3a96f4c commit 94977b9

File tree

3 files changed

+36
-9
lines changed

3 files changed

+36
-9
lines changed

React/Modules/RCTAlertManager.m

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ @interface RCTAlertManager()
3030
@implementation RCTAlertManager
3131
{
3232
NSHashTable *_alertControllers;
33+
UIWindow *_window;
3334
}
3435

3536
RCT_EXPORT_MODULE()
@@ -90,11 +91,16 @@ - (void)invalidate
9091
}
9192
}
9293

93-
UIViewController *presentingController = RCTPresentedViewController();
94-
if (presentingController == nil) {
95-
RCTLogError(@"Tried to display alert view but there is no application window. args: %@", args);
96-
return;
97-
}
94+
CGSize screenSize = [UIScreen mainScreen].bounds.size;
95+
self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, screenSize.height)];
96+
#if TARGET_OS_TV
97+
self->_window.windowLevel = UIWindowLevelNormal + 1;
98+
#else
99+
self->_window.windowLevel = UIWindowLevelStatusBar + 1;
100+
#endif
101+
UIViewController *presentingController = [UIViewController new];
102+
self->_window.rootViewController = presentingController;
103+
self->_window.hidden = NO;
98104

99105
UIAlertController *alertController = [UIAlertController
100106
alertControllerWithTitle:title
@@ -152,6 +158,7 @@ - (void)invalidate
152158
[alertController addAction:[UIAlertAction actionWithTitle:buttonTitle
153159
style:buttonStyle
154160
handler:^(__unused UIAlertAction *action) {
161+
self->_window = nil;
155162
switch (type) {
156163
case RCTAlertViewStylePlainTextInput:
157164
case RCTAlertViewStyleSecureTextInput:

React/Views/RCTModalHostView.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ - (void)didUpdateReactSubviews
159159
- (void)dismissModalViewController
160160
{
161161
if (_isPresented) {
162-
[_delegate dismissModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
162+
[_delegate dismissModalHostView:self
163+
withViewController:_modalViewController
164+
animated:[self hasAnimationType]];
163165
_isPresented = NO;
164166
}
165167
}
@@ -173,7 +175,7 @@ - (void)didMoveToWindow
173175
if (!self.userInteractionEnabled && ![self.superview.reactSubviews containsObject:self]) {
174176
return;
175177
}
176-
178+
177179
if (!_isPresented && self.window) {
178180
RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller");
179181

React/Views/RCTModalHostViewManager.m

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,19 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView withViewControlle
7575
if (_presentationBlock) {
7676
_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock);
7777
} else {
78-
[[modalHostView reactViewController] presentViewController:viewController animated:animated completion:completionBlock];
78+
UIViewController *topViewController = [modalHostView reactViewController];
79+
while (topViewController.presentedViewController != nil) {
80+
if ([topViewController.presentedViewController isKindOfClass:UIAlertController.class]) {
81+
// Don't present on top of UIAlertController, this will mess it up:
82+
// https://stackoverflow.com/questions/27028983/uialertcontroller-is-moved-to-buggy-position-at-top-of-screen-when-it-calls-pre
83+
[topViewController dismissViewControllerAnimated:animated completion:^{
84+
[topViewController presentViewController:viewController animated:animated completion:completionBlock];
85+
}];
86+
return;
87+
}
88+
topViewController = topViewController.presentedViewController;
89+
}
90+
[topViewController presentViewController:viewController animated:animated completion:completionBlock];
7991
}
8092
}
8193

@@ -89,7 +101,13 @@ - (void)dismissModalHostView:(RCTModalHostView *)modalHostView withViewControlle
89101
if (_dismissalBlock) {
90102
_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
91103
} else {
92-
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
104+
if (viewController.presentedViewController && viewController.presentingViewController) {
105+
// Ask the presenting view controller to dismiss any view controllers presented on top of the modal host
106+
// together with the host itself.
107+
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
108+
} else {
109+
[viewController dismissViewControllerAnimated:animated completion:completionBlock];
110+
}
93111
}
94112
}
95113

0 commit comments

Comments
 (0)