Browse Source

Fix default modalPresentationStyle on iOS 13 (#5709)

* Set  as default modalPresentationStyle

* Sets the system default modalPresentationStyle

* Fix tests

* Fix unit tests

* Fix unit tests

* Add modal.swipeToDismiss option
Yogev Ben David 5 years ago
parent
commit
659a42cde1
No account linked to committer's email address

+ 4
- 1
docs/docs/styling.md View File

@@ -68,7 +68,7 @@ Navigation.mergeOptions(this.props.componentId, {
68 68
     backgroundColor: 'white',
69 69
     orientation: ['portrait', 'landscape'] // An array of supported orientations
70 70
   },
71
-  modalPresentationStyle: 'overCurrentContext', // Supported styles are: 'formSheet', 'pageSheet', 'overFullScreen', 'overCurrentContext', 'currentContext', 'popover', 'fullScreen' and 'none'. On Android, only overCurrentContext and none are supported.
71
+  modalPresentationStyle: 'overCurrentContext', // Supported styles are: 'default', 'formSheet', 'pageSheet', 'overFullScreen', 'overCurrentContext', 'currentContext', 'popover', 'fullScreen' and 'none'. On Android, only overCurrentContext and none are supported.
72 72
   topBar: {
73 73
     visible: true,
74 74
     animate: false, // Controls whether TopBar visibility changes should be animated
@@ -175,6 +175,9 @@ Navigation.mergeOptions(this.props.componentId, {
175 175
     interceptTouchOutside: true,
176 176
     handleKeyboardEvents: true
177 177
   },
178
+  modal: {
179
+    swipeToDismiss: true
180
+  }
178 181
   preview: {
179 182
     reactTag: 0, // result from findNodeHandle(ref)
180 183
     width: 100,

+ 26
- 15
lib/ios/RCTConvert+Modal.h View File

@@ -2,27 +2,38 @@
2 2
 
3 3
 @interface RCTConvert (Modal)
4 4
 
5
++ (UIModalPresentationStyle)defaultModalPresentationStyle;
6
+
5 7
 @end
6 8
 
7 9
 @implementation RCTConvert (Modal)
8 10
 
11
++ (UIModalPresentationStyle)defaultModalPresentationStyle {
12
+    if (@available(iOS 13.0, *)) {
13
+        return UIModalPresentationAutomatic;
14
+    } else {
15
+        return UIModalPresentationOverFullScreen;
16
+    }
17
+}
18
+
9 19
 RCT_ENUM_CONVERTER(UIModalTransitionStyle,
10
-				   (@{@"coverVertical": @(UIModalTransitionStyleCoverVertical),
11
-					  @"flipHorizontal": @(UIModalTransitionStyleFlipHorizontal),
12
-					  @"crossDissolve": @(UIModalTransitionStyleCrossDissolve),
13
-					  @"partialCurl": @(UIModalTransitionStylePartialCurl)
14
-					  }), UIModalTransitionStyleCoverVertical, integerValue)
20
+                   (@{@"coverVertical": @(UIModalTransitionStyleCoverVertical),
21
+                      @"flipHorizontal": @(UIModalTransitionStyleFlipHorizontal),
22
+                      @"crossDissolve": @(UIModalTransitionStyleCrossDissolve),
23
+                      @"partialCurl": @(UIModalTransitionStylePartialCurl)
24
+                   }), UIModalTransitionStyleCoverVertical, integerValue)
15 25
 
16 26
 RCT_ENUM_CONVERTER(UIModalPresentationStyle,
17
-				   (@{@"fullScreen": @(UIModalPresentationFullScreen),
18
-					  @"pageSheet": @(UIModalPresentationPageSheet),
19
-					  @"formSheet": @(UIModalPresentationFormSheet),
20
-					  @"currentContext": @(UIModalPresentationCurrentContext),
21
-					  @"custom": @(UIModalPresentationCustom),
22
-					  @"overFullScreen": @(UIModalPresentationOverFullScreen),
23
-					  @"overCurrentContext": @(UIModalPresentationOverCurrentContext),
24
-					  @"popover": @(UIModalPresentationPopover),
25
-					  @"none": @(UIModalPresentationNone)
26
-					  }), UIModalPresentationFullScreen, integerValue)
27
+                   (@{@"fullScreen": @(UIModalPresentationFullScreen),
28
+                      @"pageSheet": @(UIModalPresentationPageSheet),
29
+                      @"formSheet": @(UIModalPresentationFormSheet),
30
+                      @"currentContext": @(UIModalPresentationCurrentContext),
31
+                      @"custom": @(UIModalPresentationCustom),
32
+                      @"overFullScreen": @(UIModalPresentationOverFullScreen),
33
+                      @"overCurrentContext": @(UIModalPresentationOverCurrentContext),
34
+                      @"popover": @(UIModalPresentationPopover),
35
+                      @"none": @(UIModalPresentationNone),
36
+                      @"default": @([RCTConvert defaultModalPresentationStyle])
37
+                   }), UIModalPresentationFullScreen, integerValue)
27 38
 @end
28 39
 

+ 9
- 1
lib/ios/RNNBasePresenter.m View File

@@ -5,6 +5,7 @@
5 5
 #import "UIViewController+LayoutProtocol.h"
6 6
 #import "DotIndicatorOptions.h"
7 7
 #import "RNNDotIndicatorPresenter.h"
8
+#import "RCTConvert+Modal.h"
8 9
 
9 10
 @interface RNNBasePresenter ()
10 11
 @property(nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter;
@@ -28,7 +29,14 @@
28 29
 }
29 30
 
30 31
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions {
31
-
32
+    UIViewController* viewController = self.boundViewController;
33
+    RNNNavigationOptions *withDefault = [initialOptions withDefault:[self defaultOptions]];
34
+    [viewController setModalPresentationStyle:[RCTConvert UIModalPresentationStyle:[withDefault.modalPresentationStyle getWithDefaultValue:@"default"]]];
35
+    [viewController setModalTransitionStyle:[RCTConvert UIModalTransitionStyle:[withDefault.modalTransitionStyle getWithDefaultValue:@"coverVertical"]]];
36
+    
37
+    if (@available(iOS 13.0, *)) {
38
+        viewController.modalInPresentation = ![withDefault.modal.swipeToDismiss getWithDefaultValue:YES];
39
+    }
32 40
 }
33 41
 
34 42
 - (void)applyOptionsOnViewDidLayoutSubviews:(RNNNavigationOptions *)options {

+ 1
- 0
lib/ios/RNNBottomTabsPresenter.m View File

@@ -6,6 +6,7 @@
6 6
 @implementation RNNBottomTabsPresenter
7 7
 
8 8
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
9
+    [super applyOptionsOnInit:options];
9 10
     UITabBarController *bottomTabs = self.boundViewController;
10 11
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
11 12
     [bottomTabs setCurrentTabIndex:[withDefault.bottomTabs.currentTabIndex getWithDefaultValue:0]];

+ 0
- 2
lib/ios/RNNComponentPresenter.m View File

@@ -64,8 +64,6 @@
64 64
 	
65 65
 	UIViewController* viewController = self.boundViewController;
66 66
 	RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
67
-	[viewController setModalPresentationStyle:[RCTConvert UIModalPresentationStyle:[withDefault.modalPresentationStyle getWithDefaultValue:@"fullScreen"]]];
68
-	[viewController setModalTransitionStyle:[RCTConvert UIModalTransitionStyle:[withDefault.modalTransitionStyle getWithDefaultValue:@"coverVertical"]]];
69 67
 	[viewController setDrawBehindTopBar:[withDefault.topBar.drawBehind getWithDefaultValue:NO]];
70 68
 	[viewController setDrawBehindTabBar:[withDefault.bottomTabs.drawBehind getWithDefaultValue:NO] || ![withDefault.bottomTabs.visible getWithDefaultValue:YES]];
71 69
 	

+ 7
- 0
lib/ios/RNNModalOptions.h View File

@@ -0,0 +1,7 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface RNNModalOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) Bool* swipeToDismiss;
6
+
7
+@end

+ 11
- 0
lib/ios/RNNModalOptions.m View File

@@ -0,0 +1,11 @@
1
+#import "RNNModalOptions.h"
2
+
3
+@implementation RNNModalOptions
4
+
5
+- (instancetype)initWithDict:(NSDictionary *)dict {
6
+    self = [super init];
7
+    self.swipeToDismiss = [BoolParser parse:dict key:@"swipeToDismiss"];
8
+    return self;
9
+}
10
+
11
+@end

+ 2
- 0
lib/ios/RNNNavigationOptions.h View File

@@ -11,6 +11,7 @@
11 11
 #import "RNNPreviewOptions.h"
12 12
 #import "RNNLayoutOptions.h"
13 13
 #import "RNNSplitViewOptions.h"
14
+#import "RNNModalOptions.h"
14 15
 
15 16
 extern const NSInteger BLUR_TOPBAR_TAG;
16 17
 extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
@@ -30,6 +31,7 @@ extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
30 31
 @property (nonatomic, strong) RNNPreviewOptions* preview;
31 32
 @property (nonatomic, strong) RNNLayoutOptions* layout;
32 33
 @property (nonatomic, strong) RNNSplitViewOptions* splitView;
34
+@property (nonatomic, strong) RNNModalOptions* modal;
33 35
 
34 36
 @property (nonatomic, strong) Bool* popGesture;
35 37
 @property (nonatomic, strong) Image* backgroundImage;

+ 1
- 0
lib/ios/RNNNavigationOptions.m View File

@@ -29,6 +29,7 @@
29 29
 	self.statusBar = [[RNNStatusBarOptions alloc] initWithDict:dict[@"statusBar"]];
30 30
 	self.preview = [[RNNPreviewOptions alloc] initWithDict:dict[@"preview"]];
31 31
 	self.layout = [[RNNLayoutOptions alloc] initWithDict:dict[@"layout"]];
32
+    self.modal = [[RNNModalOptions alloc] initWithDict:dict[@"modal"]];
32 33
 	
33 34
 	self.popGesture = [[Bool alloc] initWithValue:dict[@"popGesture"]];
34 35
 	

+ 8
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

@@ -208,6 +208,8 @@
208 208
 		509B258F2178BE7A00C83C23 /* RNNStackPresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 509B258E2178BE7A00C83C23 /* RNNStackPresenterTest.m */; };
209 209
 		50A00C37200F84D6000F01A6 /* RNNOverlayOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50A00C35200F84D6000F01A6 /* RNNOverlayOptions.h */; };
210 210
 		50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50A00C36200F84D6000F01A6 /* RNNOverlayOptions.m */; };
211
+		50A246372395399700A192C5 /* RNNModalOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50A246352395399700A192C5 /* RNNModalOptions.h */; };
212
+		50A246382395399700A192C5 /* RNNModalOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50A246362395399700A192C5 /* RNNModalOptions.m */; };
211 213
 		50A409CA238D444900D5FF7D /* UINavigationBar+utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 50A409C8238D444900D5FF7D /* UINavigationBar+utils.h */; };
212 214
 		50A409CB238D444900D5FF7D /* UINavigationBar+utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 50A409C9238D444900D5FF7D /* UINavigationBar+utils.m */; };
213 215
 		50AB0B1C2255F8640039DAED /* UIViewController+LayoutProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 50AB0B1A2255F8640039DAED /* UIViewController+LayoutProtocol.h */; };
@@ -562,6 +564,8 @@
562 564
 		509B258E2178BE7A00C83C23 /* RNNStackPresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNStackPresenterTest.m; sourceTree = "<group>"; };
563 565
 		50A00C35200F84D6000F01A6 /* RNNOverlayOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNOverlayOptions.h; sourceTree = "<group>"; };
564 566
 		50A00C36200F84D6000F01A6 /* RNNOverlayOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNOverlayOptions.m; sourceTree = "<group>"; };
567
+		50A246352395399700A192C5 /* RNNModalOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNModalOptions.h; sourceTree = "<group>"; };
568
+		50A246362395399700A192C5 /* RNNModalOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNModalOptions.m; sourceTree = "<group>"; };
565 569
 		50A409C8238D444900D5FF7D /* UINavigationBar+utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UINavigationBar+utils.h"; sourceTree = "<group>"; };
566 570
 		50A409C9238D444900D5FF7D /* UINavigationBar+utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UINavigationBar+utils.m"; sourceTree = "<group>"; };
567 571
 		50AB0B1A2255F8640039DAED /* UIViewController+LayoutProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+LayoutProtocol.h"; sourceTree = "<group>"; };
@@ -963,6 +967,8 @@
963 967
 				506317AD220B550600B26FC3 /* RNNInsetsOptions.m */,
964 968
 				309876223177761614786DCC /* DotIndicatorOptions.m */,
965 969
 				30987122507D8CBF16624F93 /* DotIndicatorOptions.h */,
970
+				50A246352395399700A192C5 /* RNNModalOptions.h */,
971
+				50A246362395399700A192C5 /* RNNModalOptions.m */,
966 972
 			);
967 973
 			name = Options;
968 974
 			sourceTree = "<group>";
@@ -1384,6 +1390,7 @@
1384 1390
 				5049595A216F6B46006D2B81 /* NullDictionary.h in Headers */,
1385 1391
 				501224062173592D000F5F98 /* RNNBottomTabsPresenter.h in Headers */,
1386 1392
 				50706E6D20CE7CA5003345C3 /* UIImage+tint.h in Headers */,
1393
+				50A246372395399700A192C5 /* RNNModalOptions.h in Headers */,
1387 1394
 				309874B40D202C9718F15CBD /* UIView+Utils.h in Headers */,
1388 1395
 				309877B0B5AAA7788F56F3D9 /* UIViewController+Utils.h in Headers */,
1389 1396
 				30987680135A8C78E62D5B8E /* DotIndicatorOptions.h in Headers */,
@@ -1568,6 +1575,7 @@
1568 1575
 				7BA500751E2544B9001B9E1B /* ReactNativeNavigation.m in Sources */,
1569 1576
 				50E5F7962240EBD6002AFEAD /* RNNAnimationsTransitionDelegate.m in Sources */,
1570 1577
 				5038A3BA216DFCFD009280BC /* UITabBarController+RNNOptions.m in Sources */,
1578
+				50A246382395399700A192C5 /* RNNModalOptions.m in Sources */,
1571 1579
 				263905B21E4C6F440023D7D3 /* MMDrawerController.m in Sources */,
1572 1580
 				50644A2120E11A720026709C /* Constants.m in Sources */,
1573 1581
 				501223D82173590F000F5F98 /* RNNStackPresenter.m in Sources */,

+ 12
- 0
lib/src/interfaces/Options.ts View File

@@ -683,6 +683,14 @@ export interface OverlayOptions {
683 683
   handleKeyboardEvents?: boolean;
684 684
 }
685 685
 
686
+export interface ModalOptions {
687
+  /**
688
+   * Control wether this modal should be dismiss using swipe gesture when the modalPresentationStyle = 'pageSheet'
689
+   * #### (iOS specific)
690
+   */
691
+  swipeToDismiss?: boolean;
692
+}
693
+
686 694
 export interface OptionsPreviewAction {
687 695
   /**
688 696
    * Reference ID to get callbacks from
@@ -961,6 +969,10 @@ export interface Options {
961 969
    * Configure the overlay
962 970
    */
963 971
   overlay?: OverlayOptions;
972
+  /**
973
+   * Configure the modal
974
+   */
975
+  modal?: ModalOptions;
964 976
   /**
965 977
    * Animation used for navigation commands that modify the layout
966 978
    * hierarchy can be controlled in options.

+ 8
- 0
playground/ios/NavigationTests/RNNBasePresenterTest.m View File

@@ -89,5 +89,13 @@
89 89
     XCTAssertEqual([_uut getStatusBarStyle:options], UIStatusBarStyleLightContent);
90 90
 }
91 91
 
92
+- (void)testApplyOptionsOnInit_setSwipeToDismiss {
93
+    self.options.modal.swipeToDismiss = [[Bool alloc] initWithBOOL:NO];
94
+	XCTAssertFalse(_boundViewController.modalInPresentation);
95
+    [self.uut applyOptionsOnInit:self.options];
96
+	XCTAssertTrue(_boundViewController.modalInPresentation);
97
+}
98
+
99
+
92 100
 
93 101
 @end

+ 2
- 1
playground/ios/NavigationTests/RNNComponentPresenterTest.m View File

@@ -5,6 +5,7 @@
5 5
 #import "RNNComponentViewController.h"
6 6
 #import "UIViewController+LayoutProtocol.h"
7 7
 #import "RNNTitleViewHelper.h"
8
+#import "RCTConvert+Modal.h"
8 9
 
9 10
 @interface RNNComponentPresenterTest : XCTestCase
10 11
 
@@ -78,7 +79,7 @@
78 79
 }
79 80
 
80 81
 - (void)testApplyOptionsOnInit_shouldSetModalPresentationStyleWithDefault {
81
-    [(UIViewController *) [(id) self.boundViewController expect] setModalPresentationStyle:UIModalPresentationFullScreen];
82
+    [(UIViewController *) [(id) self.boundViewController expect] setModalPresentationStyle:[RCTConvert defaultModalPresentationStyle]];
82 83
 	[self.uut applyOptionsOnInit:self.options];
83 84
 	[(id)self.boundViewController verify];
84 85
 }

+ 9
- 0
playground/ios/NavigationTests/RNNTabBarControllerTest.m View File

@@ -3,6 +3,7 @@
3 3
 #import "RNNComponentViewController.h"
4 4
 #import "RNNStackController.h"
5 5
 #import <OCMock/OCMock.h>
6
+#import "RCTConvert+Modal.h"
6 7
 
7 8
 @interface RNNTabBarControllerTest : XCTestCase
8 9
 
@@ -75,6 +76,13 @@
75 76
     XCTAssertTrue(uut.delegate == uut);
76 77
 }
77 78
 
79
+- (void)testInitWithLayoutInfo_shouldCreateWithDefaultStyles {
80
+    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNBottomTabsPresenter alloc] init] eventEmitter:nil childViewControllers:nil];
81
+	
82
+    XCTAssertEqual(uut.modalPresentationStyle, UIModalPresentationPageSheet);
83
+	XCTAssertEqual(uut.modalTransitionStyle, UIModalTransitionStyleCoverVertical);
84
+}
85
+
78 86
 - (void)testWillMoveToParent_shouldNotInvokePresenterApplyOptionsOnWillMoveToNilParent {
79 87
     [[self.mockTabBarPresenter reject] applyOptionsOnWillMoveToParentViewController:[self.uut options]];
80 88
     [self.uut willMoveToParentViewController:nil];
@@ -161,4 +169,5 @@
161 169
     XCTAssertTrue(uut.selectedIndex == 1);
162 170
 }
163 171
 
172
+
164 173
 @end

+ 2
- 2
playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m View File

@@ -26,10 +26,10 @@
26 26
     RNNComponentPresenter* presenter = [[RNNComponentPresenter alloc] init];
27 27
     RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initEmptyOptions];
28 28
     RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
29
-    defaultOptions.modalPresentationStyle = [[Text alloc] initWithValue:@"fullScreen"];
29
+    defaultOptions.modalPresentationStyle = [[Text alloc] initWithValue:@"default"];
30 30
 
31 31
     UIViewController* uut = [[UIViewController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:defaultOptions presenter:presenter eventEmitter:nil childViewControllers:nil];
32
-    XCTAssertEqual(uut.modalPresentationStyle, [RCTConvert UIModalPresentationStyle:@"fullScreen"]);
32
+    XCTAssertEqual(uut.modalPresentationStyle, UIModalPresentationPageSheet);
33 33
 }
34 34
 
35 35
 - (void)testInitWithLayoutInfoShouldSetChildViewControllers {

+ 2
- 2
playground/ios/Podfile.lock View File

@@ -220,7 +220,7 @@ PODS:
220 220
     - ReactCommon/jscallinvoker (= 0.61.4)
221 221
   - ReactNativeKeyboardTrackingView (5.6.1):
222 222
     - React
223
-  - ReactNativeNavigation (3.7.0):
223
+  - ReactNativeNavigation (4.0.2):
224 224
     - React
225 225
   - Yoga (1.14.0)
226 226
 
@@ -346,7 +346,7 @@ SPEC CHECKSUMS:
346 346
   React-RCTVibration: 0f76400ee3cec6edb9c125da49fed279340d145a
347 347
   ReactCommon: a6a294e7028ed67b926d29551aa9394fd989c24c
348 348
   ReactNativeKeyboardTrackingView: a240a6a0dba852bb107109a7ec7e98b884055977
349
-  ReactNativeNavigation: bed6f504aa1a4dadd151503bf99d0ef96d06847f
349
+  ReactNativeNavigation: f3da2b103f01bc576bbb1a41df67486788dcf6af
350 350
   Yoga: ba3d99dbee6c15ea6bbe3783d1f0cb1ffb79af0f
351 351
 
352 352
 PODFILE CHECKSUM: 8a6eee15d75935b9144e5228ce8fa2a5b98079e7

+ 2
- 1
playground/src/commons/Options.js View File

@@ -21,7 +21,8 @@ const setDefaultOptions = () => Navigation.setDefaultOptions({
21 21
   },
22 22
   animations: {
23 23
     ...useSlowOpenScreenAnimations ? slowOpenScreenAnimations : {}   
24
-  }
24
+  },
25
+  modalPresentationStyle: 'fullScreen'
25 26
 });
26 27
 
27 28
 const slowOpenScreenAnimations = {