diff --git a/packages/react-native/React/Views/RCTModalHostView.h b/packages/react-native/React/Views/RCTModalHostView.h index 2fcdcaea83f5b2..17bd1d0092b92b 100644 --- a/packages/react-native/React/Views/RCTModalHostView.h +++ b/packages/react-native/React/Views/RCTModalHostView.h @@ -36,11 +36,13 @@ @property (nonatomic, copy) NSArray *supportedOrientations; @property (nonatomic, copy) RCTDirectEventBlock onOrientationChange; +@property (nonatomic, strong) UIWindow *modalWindow; // Fabric only @property (nonatomic, copy) RCTDirectEventBlock onDismiss; - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; +- (void)dismissModalViewControllerWithCompletion:(void (^)(void))completion; @end @@ -52,5 +54,8 @@ - (void)dismissModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated; +- (void)dismissModalHostViewWithCompletion:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated completion: (void (^)(void))completion; @end diff --git a/packages/react-native/React/Views/RCTModalHostView.m b/packages/react-native/React/Views/RCTModalHostView.m index dfde4ae47ab137..2ca8dc0d30fb3c 100644 --- a/packages/react-native/React/Views/RCTModalHostView.m +++ b/packages/react-native/React/Views/RCTModalHostView.m @@ -40,6 +40,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge _modalViewController.view = containerView; _touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge]; _isPresented = NO; + _modalViewController.modalHostView = self; __weak typeof(self) weakSelf = self; _modalViewController.boundsDidChangeBlock = ^(CGRect newBounds) { @@ -115,9 +116,14 @@ - (void)didUpdateReactSubviews } - (void)dismissModalViewController +{ + [self dismissModalViewControllerWithCompletion: nil]; +} + +- (void)dismissModalViewControllerWithCompletion:(void (^)(void))completion { if (_isPresented) { - [_delegate dismissModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]]; + [_delegate dismissModalHostViewWithCompletion:self withViewController:_modalViewController animated:[self hasAnimationType] completion: completion]; _isPresented = NO; } } diff --git a/packages/react-native/React/Views/RCTModalHostViewController.h b/packages/react-native/React/Views/RCTModalHostViewController.h index b12b0f7fc15129..ec340e8073a7d5 100644 --- a/packages/react-native/React/Views/RCTModalHostViewController.h +++ b/packages/react-native/React/Views/RCTModalHostViewController.h @@ -6,11 +6,14 @@ */ #import +#import "RCTModalHostView.h" @interface RCTModalHostViewController : UIViewController @property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); +@property RCTModalHostView* modalHostView; + @property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; @end diff --git a/packages/react-native/React/Views/RCTModalHostViewManager.m b/packages/react-native/React/Views/RCTModalHostViewManager.m index 4b9f9ad7267c8f..d4f91347acaf61 100644 --- a/packages/react-native/React/Views/RCTModalHostViewManager.m +++ b/packages/react-native/React/Views/RCTModalHostViewManager.m @@ -79,31 +79,70 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView if (self->_presentationBlock) { self->_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { - [[modalHostView reactViewController] presentViewController:viewController - animated:animated - completion:completionBlock]; + UIViewController* presentingViewController; + // pageSheet and formSheet presentation style animate the presented view so we need to use the last presented view controller + // For other presentation styles we use the new window + if (modalHostView.presentationStyle == UIModalPresentationPageSheet || modalHostView.presentationStyle == UIModalPresentationFormSheet) { + UIViewController *lastPresentedViewController = RCTKeyWindow().rootViewController; + UIViewController *presentedViewController = nil; + while (lastPresentedViewController != nil) { + presentedViewController = lastPresentedViewController; + lastPresentedViewController = lastPresentedViewController.presentedViewController; + } + presentingViewController = presentedViewController; + } else { + modalHostView.modalWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; + modalHostView.modalWindow.windowLevel = UIWindowLevelAlert; + UIViewController *newViewController = [[UIViewController alloc] init]; + modalHostView.modalWindow.rootViewController = newViewController; + [modalHostView.modalWindow makeKeyAndVisible]; + presentingViewController = newViewController; + } + [presentingViewController presentViewController:viewController animated:animated completion:completionBlock]; } }); } -- (void)dismissModalHostView:(RCTModalHostView *)modalHostView +- (void)dismissModalHostViewWithCompletion:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController - animated:(BOOL)animated + animated:(BOOL)animated completion:(void (^)(void))completion { dispatch_block_t completionBlock = ^{ if (modalHostView.identifier) { [[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier]; } + if (completion) { + completion(); + } + modalHostView.modalWindow = nil; }; dispatch_async(dispatch_get_main_queue(), ^{ if (self->_dismissalBlock) { self->_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { - [viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock]; + // Will be true for pageSheet and formSheet presentation styles + // We dismiss the nested modal and then dismiss the current modal + if (viewController.presentedViewController != nil && [viewController.presentedViewController isKindOfClass:[RCTModalHostViewController class]]) { + RCTModalHostViewController* presentedModalViewController = (RCTModalHostViewController *)viewController.presentedViewController; + dispatch_block_t childModalCompletionBlock = ^{ + [viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock]; + }; + + [presentedModalViewController.modalHostView dismissModalViewControllerWithCompletion: childModalCompletionBlock]; + } else { + [viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock]; + } } }); } +- (void)dismissModalHostView:(RCTModalHostView *)modalHostView + withViewController:(RCTModalHostViewController *)viewController + animated:(BOOL)animated +{ + [self dismissModalHostViewWithCompletion:modalHostView withViewController:viewController animated:animated completion:nil]; +} + - (RCTShadowView *)shadowView { return [RCTModalHostShadowView new];