Skip to content

Instantly share code, notes, and snippets.

@ps73
Last active July 21, 2021 18:42
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ps73/012b8b97bb7866db4b2b4636a8396d98 to your computer and use it in GitHub Desktop.
Save ps73/012b8b97bb7866db4b2b4636a8396d98 to your computer and use it in GitHub Desktop.
React Native 0.63.3 iOS Modal Swipe Down Bugfix
diff --git a/node_modules/react-native/React/Views/RCTModalHostView.h b/node_modules/react-native/React/Views/RCTModalHostView.h
index 4e61886..2b8b6c0 100644
--- a/node_modules/react-native/React/Views/RCTModalHostView.h
+++ b/node_modules/react-native/React/Views/RCTModalHostView.h
@@ -17,7 +17,7 @@
@protocol RCTModalHostViewInteractor;
-@interface RCTModalHostView : UIView <RCTInvalidating>
+@interface RCTModalHostView : UIView <RCTInvalidating, UIAdaptivePresentationControllerDelegate>
@property (nonatomic, copy) NSString *animationType;
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle;
@@ -31,9 +31,9 @@
@property (nonatomic, copy) NSArray<NSString *> *supportedOrientations;
@property (nonatomic, copy) RCTDirectEventBlock onOrientationChange;
+@property (nonatomic, copy) RCTDirectEventBlock onRequestClose;
#if TARGET_OS_TV
-@property (nonatomic, copy) RCTDirectEventBlock onRequestClose;
@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler;
#endif
diff --git a/node_modules/react-native/React/Views/RCTModalHostView.m b/node_modules/react-native/React/Views/RCTModalHostView.m
index 6a15330..9ed68db 100644
--- a/node_modules/react-native/React/Views/RCTModalHostView.m
+++ b/node_modules/react-native/React/Views/RCTModalHostView.m
@@ -41,6 +41,9 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
if ((self = [super initWithFrame:CGRectZero])) {
_bridge = bridge;
_modalViewController = [RCTModalHostViewController new];
+ if (@available(iOS 13.0, *)) {
+ _modalViewController.presentationController.delegate = self;
+ }
UIView *containerView = [UIView new];
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_modalViewController.view = containerView;
@@ -62,6 +65,22 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
return self;
}
+- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection
+{
+ if (self.presentationStyle == UIModalPresentationFullScreen && self.isTransparent) {
+ return UIModalPresentationOverFullScreen;
+ }
+ return self.presentationStyle;
+}
+
+- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
+{
+ if (self.presentationStyle == UIModalPresentationFullScreen && self.isTransparent) {
+ return UIModalPresentationOverFullScreen;
+ }
+ return self.presentationStyle;
+}
+
#if TARGET_OS_TV
- (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer
{
@@ -69,10 +88,12 @@ - (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer
_onRequestClose(nil);
}
}
+#endif
- (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
{
_onRequestClose = onRequestClose;
+ #if TARGET_OS_TV
if (_reactSubview) {
if (_onRequestClose && _menuButtonGestureRecognizer) {
[_reactSubview addGestureRecognizer:_menuButtonGestureRecognizer];
@@ -80,8 +101,8 @@ - (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
[_reactSubview removeGestureRecognizer:_menuButtonGestureRecognizer];
}
}
+ #endif
}
-#endif
- (void)notifyForBoundsChange:(CGRect)newBounds
{
@@ -155,6 +176,14 @@ - (void)didUpdateReactSubviews
// Do nothing, as subview (singular) is managed by `insertReactSubview:atIndex:`
}
+- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
+{
+ if (_onRequestClose) {
+ _onRequestClose(nil);
+ }
+}
+
+
- (void)dismissModalViewController
{
if (_isPresented) {
@@ -187,6 +216,12 @@ - (void)didMoveToWindow
if (self.presentationStyle != UIModalPresentationNone) {
_modalViewController.modalPresentationStyle = self.presentationStyle;
}
+ // only handle swipe close when onRequest method is provided
+ if (@available(iOS 13.0, *)) {
+ if (_onRequestClose) {
+ _modalViewController.modalInPresentation = self.presentationStyle != UIModalPresentationFormSheet && self.presentationStyle != UIModalPresentationPageSheet;
+ }
+ }
[_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
_isPresented = YES;
}
diff --git a/node_modules/react-native/React/Views/RCTModalHostViewManager.m b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
index bafab9d..d56bb52 100644
--- a/node_modules/react-native/React/Views/RCTModalHostViewManager.m
+++ b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
@@ -116,9 +116,6 @@ - (void)invalidate
RCT_EXPORT_VIEW_PROPERTY(identifier, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
-
-#if TARGET_OS_TV
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)
-#endif
@end
diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env
new file mode 100644
index 0000000..361f5fb
--- /dev/null
+++ b/node_modules/react-native/scripts/.packager.env
@@ -0,0 +1 @@
+export RCT_METRO_PORT=8081
diff --git a/node_modules/react-native/React/Views/RCTModalHostView.h b/node_modules/react-native/React/Views/RCTModalHostView.h
index 4e61886..2b8b6c0 100644
--- a/node_modules/react-native/React/Views/RCTModalHostView.h
+++ b/node_modules/react-native/React/Views/RCTModalHostView.h
@@ -17,7 +17,7 @@
@protocol RCTModalHostViewInteractor;
-@interface RCTModalHostView : UIView <RCTInvalidating>
+@interface RCTModalHostView : UIView <RCTInvalidating, UIAdaptivePresentationControllerDelegate>
@property (nonatomic, copy) NSString *animationType;
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle;
@@ -31,9 +31,9 @@
@property (nonatomic, copy) NSArray<NSString *> *supportedOrientations;
@property (nonatomic, copy) RCTDirectEventBlock onOrientationChange;
+@property (nonatomic, copy) RCTDirectEventBlock onRequestClose;
#if TARGET_OS_TV
-@property (nonatomic, copy) RCTDirectEventBlock onRequestClose;
@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler;
#endif
diff --git a/node_modules/react-native/React/Views/RCTModalHostView.m b/node_modules/react-native/React/Views/RCTModalHostView.m
index 6a15330..9ed68db 100644
--- a/node_modules/react-native/React/Views/RCTModalHostView.m
+++ b/node_modules/react-native/React/Views/RCTModalHostView.m
@@ -41,6 +41,9 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
if ((self = [super initWithFrame:CGRectZero])) {
_bridge = bridge;
_modalViewController = [RCTModalHostViewController new];
+ if (@available(iOS 13.0, *)) {
+ _modalViewController.presentationController.delegate = self;
+ }
UIView *containerView = [UIView new];
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_modalViewController.view = containerView;
@@ -62,6 +65,22 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
return self;
}
+- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection
+{
+ if (self.presentationStyle == UIModalPresentationFullScreen && self.isTransparent) {
+ return UIModalPresentationOverFullScreen;
+ }
+ return self.presentationStyle;
+}
+
+- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
+{
+ if (self.presentationStyle == UIModalPresentationFullScreen && self.isTransparent) {
+ return UIModalPresentationOverFullScreen;
+ }
+ return self.presentationStyle;
+}
+
#if TARGET_OS_TV
- (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer
{
@@ -69,10 +88,12 @@ - (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer
_onRequestClose(nil);
}
}
+#endif
- (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
{
_onRequestClose = onRequestClose;
+ #if TARGET_OS_TV
if (_reactSubview) {
if (_onRequestClose && _menuButtonGestureRecognizer) {
[_reactSubview addGestureRecognizer:_menuButtonGestureRecognizer];
@@ -80,8 +101,8 @@ - (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
[_reactSubview removeGestureRecognizer:_menuButtonGestureRecognizer];
}
}
+ #endif
}
-#endif
- (void)notifyForBoundsChange:(CGRect)newBounds
{
@@ -155,6 +176,14 @@ - (void)didUpdateReactSubviews
// Do nothing, as subview (singular) is managed by `insertReactSubview:atIndex:`
}
+- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
+{
+ if (_onRequestClose) {
+ _onRequestClose(nil);
+ }
+}
+
+
- (void)dismissModalViewController
{
if (_isPresented) {
@@ -187,6 +216,12 @@ - (void)didMoveToWindow
if (self.presentationStyle != UIModalPresentationNone) {
_modalViewController.modalPresentationStyle = self.presentationStyle;
}
+ // only handle swipe close when onRequest method is provided
+ if (@available(iOS 13.0, *)) {
+ if (_onRequestClose) {
+ _modalViewController.modalInPresentation = self.presentationStyle != UIModalPresentationFormSheet && self.presentationStyle != UIModalPresentationPageSheet;
+ }
+ }
[_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
_isPresented = YES;
}
diff --git a/node_modules/react-native/React/Views/RCTModalHostViewManager.m b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
index bafab9d..d56bb52 100644
--- a/node_modules/react-native/React/Views/RCTModalHostViewManager.m
+++ b/node_modules/react-native/React/Views/RCTModalHostViewManager.m
@@ -116,9 +116,6 @@ - (void)invalidate
RCT_EXPORT_VIEW_PROPERTY(identifier, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
-
-#if TARGET_OS_TV
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)
-#endif
@end
diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env
new file mode 100644
index 0000000..361f5fb
--- /dev/null
+++ b/node_modules/react-native/scripts/.packager.env
@@ -0,0 +1 @@
+export RCT_METRO_PORT=8081
@ps73
Copy link
Author

ps73 commented Oct 23, 2020

Because the swipe down gesture in the react native modal in formSheet or pageSheet on iOS is not working as expected, I have built this patch file.
So if you provide the onRequestClose property on the modal component the swipe down can be triggered and works as it should.
If you don't set the onRequestClose property the swipe down is disabled.

Install patch-package create a patches folder in your project root folder and paste the file above into it.
Then add following script to your package.json:

 "scripts": {
  ...
  "postinstall": "patch-package"
 }

EDIT:

In most cases you have to reinstall the app on the simulator or device.

@Jonatthu
Copy link

@ps73 can you create a pull request to the main branch?

@panvourtsis
Copy link

panvourtsis commented Nov 26, 2020

is this working ? i tried it but nothing changed.
Version is the same as my RN.

I try to swipe down but doesnt dismiss

Steps i took

  1. Run yarn add patch-package
  2. add this to package.json
"scripts": {
  ...
  "postinstall": "patch-package"
 }
  1. Create the folder /patches
  2. Download the patch file above and put it in patches folder
  3. Run yarn install

@ps73
Copy link
Author

ps73 commented Dec 2, 2020

@panvourtsis Do you use the onRequestClose property on the modal component? If this prop isn't set the swipe down is disabled as well as the Back Button on Android...

@panvourtsis
Copy link

yes i am using it, here is how

<RNModal
      animationType="slide"
      transparent={true}
      visible={modalVisible}
      hardwareAccelerated
      presentationStyle={'pageSheet'}
      onDismiss={() => {
        console.log('test');
      }}
      onRequestClose={() => {
        console.log('test');
      }}
    >

@ps73
Copy link
Author

ps73 commented Dec 8, 2020

@panvourtsis It isn't a problem with this gist, your postinstall is not executed by default when you are using yarn (I use npm). To get this working with yarn you have to do additional adaptations. https://yarnpkg.com/package/postinstall

Also you have to set modalVisible to false in your onRequestClose method

@panvourtsis
Copy link

@ps73 thanks for all your help, i didn everything correctly after all but for some reason on iphone it wasn't working but after a new installation it work. Thanks for your work as well

@luokuning
Copy link

Nice work, Thanks!

@ericning00
Copy link

Wow! This works incredibly well. Thanks @ps73

@whalemare
Copy link

What about 0.64.0?

@sgup
Copy link

sgup commented Jul 21, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment