From af02ce1098a80562895698d3a4a28ceb49ffaa5f Mon Sep 17 00:00:00 2001 From: Sergey Zolotarev Date: Sat, 15 Dec 2018 20:17:14 +0600 Subject: [PATCH] Fix modal and alert dismissal bugs on iOS (#22237) --- React/Modules/RCTAlertManager.m | 17 ++++++++++++----- React/Views/RCTModalHostView.h | 9 +++++++-- React/Views/RCTModalHostView.m | 8 ++++++-- React/Views/RCTModalHostViewController.m | 1 - React/Views/RCTModalHostViewManager.m | 24 +++++++++++++++++++----- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index a8f30b10a4bdac..119e8df1147912 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -30,6 +30,7 @@ @interface RCTAlertManager() @implementation RCTAlertManager { NSHashTable *_alertControllers; + UIWindow *_window; } RCT_EXPORT_MODULE() @@ -90,11 +91,16 @@ - (void)invalidate } } - UIViewController *presentingController = RCTPresentedViewController(); - if (presentingController == nil) { - RCTLogError(@"Tried to display alert view but there is no application window. args: %@", args); - return; - } + CGSize screenSize = [UIScreen mainScreen].bounds.size; + self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, screenSize.height)]; +#if TARGET_OS_TV + self->_window.windowLevel = UIWindowLevelNormal + 1; +#else + self->_window.windowLevel = UIWindowLevelStatusBar + 1; +#endif + UIViewController *presentingController = [UIViewController new]; + self->_window.rootViewController = presentingController; + self->_window.hidden = NO; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title @@ -152,6 +158,7 @@ - (void)invalidate [alertController addAction:[UIAlertAction actionWithTitle:buttonTitle style:buttonStyle handler:^(__unused UIAlertAction *action) { + self->_window = nil; switch (type) { case RCTAlertViewStylePlainTextInput: case RCTAlertViewStyleSecureTextInput: diff --git a/React/Views/RCTModalHostView.h b/React/Views/RCTModalHostView.h index e421e597f806dd..c320194d931dff 100644 --- a/React/Views/RCTModalHostView.h +++ b/React/Views/RCTModalHostView.h @@ -43,7 +43,12 @@ @protocol RCTModalHostViewInteractor -- (void)presentModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated; -- (void)dismissModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated; +- (void)presentModalHostView:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated; +- (void)dismissModalHostView:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated + completion:(void (^)(void))completionHandler; @end diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index f586e4ad0eadb5..9ec459933d64b1 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -161,8 +161,12 @@ - (void)didUpdateReactSubviews - (void)dismissModalViewController { if (_isPresented) { - [_delegate dismissModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]]; - _isPresented = NO; + [_delegate dismissModalHostView:self + withViewController:_modalViewController + animated:[self hasAnimationType] + completion:^{ + self->_isPresented = NO; + }]; } } diff --git a/React/Views/RCTModalHostViewController.m b/React/Views/RCTModalHostViewController.m index abd5369dce7afe..1659b4068c28b4 100644 --- a/React/Views/RCTModalHostViewController.m +++ b/React/Views/RCTModalHostViewController.m @@ -72,5 +72,4 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations #endif // RCT_DEV #endif // !TARGET_OS_TV - @end diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 6377683d2dc748..054527ca347f65 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -65,7 +65,9 @@ - (UIView *)view return view; } -- (void)presentModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated +- (void)presentModalHostView:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated { dispatch_block_t completionBlock = ^{ if (modalHostView.onShow) { @@ -75,25 +77,37 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView withViewControlle if (_presentationBlock) { _presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { - [[modalHostView reactViewController] presentViewController:viewController animated:animated completion:completionBlock]; + [[modalHostView reactViewController] + presentViewController:viewController animated:animated completion:completionBlock]; } } -- (void)dismissModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated +- (void)dismissModalHostView:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated + completion:(void (^)(void))completionHandler { dispatch_block_t completionBlock = ^{ if (modalHostView.identifier) { [[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier]; } + if (completionHandler) { + completionHandler(); + } }; if (_dismissalBlock) { _dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { - [viewController dismissViewControllerAnimated:animated completion:completionBlock]; + if (viewController.presentedViewController && viewController.presentingViewController) { + // Ask the presenting view controller to dismiss any view controllers presented on top of the modal host + // together with the host itself. + [viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock]; + } else { + [viewController dismissViewControllerAnimated:animated completion:completionBlock]; + } } } - - (RCTShadowView *)shadowView { return [RCTModalHostShadowView new];