From abdad8fe2b87a676ce871a8442ac98bd11a0a57a Mon Sep 17 00:00:00 2001 From: Nishan Bende Date: Sun, 9 May 2021 14:54:27 +0530 Subject: [PATCH 1/4] fix: multiple modal ios --- React/Views/RCTModalHostViewManager.m | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 91d83aabbdc604..8134e5abd95333 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -78,10 +78,22 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView if (_presentationBlock) { _presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { - [[modalHostView reactViewController] presentViewController:viewController - animated:animated - completion:completionBlock]; - } + + UIViewController *lastPresentedViewController = modalHostView.reactViewController; + UIViewController *presentedViewController = nil; + + + while (lastPresentedViewController != nil) { + presentedViewController = lastPresentedViewController; + lastPresentedViewController = lastPresentedViewController.presentedViewController; + } + + + [presentedViewController presentViewController:viewController + animated:animated + completion:completionBlock]; + } + } - (void)dismissModalHostView:(RCTModalHostView *)modalHostView From 0047c221103e4a888ef12d7f1348244160d535d5 Mon Sep 17 00:00:00 2001 From: Nishan Date: Fri, 18 Aug 2023 12:44:06 +0530 Subject: [PATCH 2/4] fix: multiple modal ios --- .../React/Views/RCTModalHostView.h | 1 + .../React/Views/RCTModalHostViewManager.m | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/react-native/React/Views/RCTModalHostView.h b/packages/react-native/React/Views/RCTModalHostView.h index 2fcdcaea83f5b2..da5ef59602cf0a 100644 --- a/packages/react-native/React/Views/RCTModalHostView.h +++ b/packages/react-native/React/Views/RCTModalHostView.h @@ -36,6 +36,7 @@ @property (nonatomic, copy) NSArray *supportedOrientations; @property (nonatomic, copy) RCTDirectEventBlock onOrientationChange; +@property (nonatomic, strong) UIWindow *modalWindow; // Fabric only @property (nonatomic, copy) RCTDirectEventBlock onDismiss; diff --git a/packages/react-native/React/Views/RCTModalHostViewManager.m b/packages/react-native/React/Views/RCTModalHostViewManager.m index 4b9f9ad7267c8f..6654bbbe3b7213 100644 --- a/packages/react-native/React/Views/RCTModalHostViewManager.m +++ b/packages/react-native/React/Views/RCTModalHostViewManager.m @@ -79,9 +79,24 @@ - (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; + 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]; } }); } From 2583d685f6591338d8052649fb229e039ae83be8 Mon Sep 17 00:00:00 2001 From: Nishan Date: Fri, 18 Aug 2023 13:44:29 +0530 Subject: [PATCH 3/4] trigger ondismiss if nested modal closes --- .../React/Views/RCTModalHostView.h | 4 +++ .../React/Views/RCTModalHostView.m | 8 +++++- .../React/Views/RCTModalHostViewController.h | 3 +++ .../React/Views/RCTModalHostViewManager.m | 26 ++++++++++++++++--- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/react-native/React/Views/RCTModalHostView.h b/packages/react-native/React/Views/RCTModalHostView.h index da5ef59602cf0a..17bd1d0092b92b 100644 --- a/packages/react-native/React/Views/RCTModalHostView.h +++ b/packages/react-native/React/Views/RCTModalHostView.h @@ -42,6 +42,7 @@ @property (nonatomic, copy) RCTDirectEventBlock onDismiss; - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; +- (void)dismissModalViewControllerWithCompletion:(void (^)(void))completion; @end @@ -53,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 6654bbbe3b7213..26b4a7b368bca5 100644 --- a/packages/react-native/React/Views/RCTModalHostViewManager.m +++ b/packages/react-native/React/Views/RCTModalHostViewManager.m @@ -101,24 +101,44 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView }); } -- (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]; + 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]; From e7d6b449f1ff8e63429665b591435322076bdbae Mon Sep 17 00:00:00 2001 From: Nishan Date: Fri, 18 Aug 2023 16:02:26 +0530 Subject: [PATCH 4/4] chore: add comments --- packages/react-native/React/Views/RCTModalHostViewManager.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-native/React/Views/RCTModalHostViewManager.m b/packages/react-native/React/Views/RCTModalHostViewManager.m index 26b4a7b368bca5..d4f91347acaf61 100644 --- a/packages/react-native/React/Views/RCTModalHostViewManager.m +++ b/packages/react-native/React/Views/RCTModalHostViewManager.m @@ -80,6 +80,8 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView self->_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { 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; @@ -118,6 +120,8 @@ - (void)dismissModalHostViewWithCompletion:(RCTModalHostView *)modalHostView if (self->_dismissalBlock) { self->_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock); } else { + // 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 = ^{