diff --git a/lib/ios/RNNCommandsHandler.m b/lib/ios/RNNCommandsHandler.m index 5eb5078892d..de5e87ddb13 100644 --- a/lib/ios/RNNCommandsHandler.m +++ b/lib/ios/RNNCommandsHandler.m @@ -369,6 +369,40 @@ - (void)showModal:(NSDictionary *)layout [_layoutManager addPendingViewController:newVc]; __weak UIViewController *weakNewVC = newVc; + + if (@available(iOS 15.0, *)) { + UISheetPresentationController *sheet = [weakNewVC sheetPresentationController]; + + if (sheet) { + [sheet setPrefersScrollingExpandsWhenScrolledToEdge:[withDefault.modal.prefersScrollingExpandsWhenScrolledToEdge withDefault:true]]; + [sheet setPrefersEdgeAttachedInCompactHeight:[withDefault.modal.prefersEdgeAttachedInCompactHeight withDefault:false]]; + [sheet setWidthFollowsPreferredContentSizeWhenEdgeAttached:[withDefault.modal.widthFollowsPreferredContentSizeWhenEdgeAttached withDefault:false]]; + [sheet setPrefersGrabberVisible:[withDefault.modal.prefersGrabberVisible withDefault:false]]; + + + [sheet setDetents:@[UISheetPresentationControllerDetent.mediumDetent, UISheetPresentationControllerDetent.largeDetent]]; + + + if (withDefault.modal.detents) { + // FIXME: This should be handled with conversion rather than straightforward usage + //[sheet setDetents:withDefault.modal.detents]; + } + // FIXME: This should be handled with conversion which is incorrect + if (withDefault.modal.largestUndimmedDetent.hasValue) { + //[sheet setLargestUndimmedDetentIdentifier:[RNNConvert UISheetPresentationControllerDetentIdentifier:[withDefault.modal.largestUndimmedDetent withDefault:nil]]]; + [sheet setLargestUndimmedDetentIdentifier:UISheetPresentationControllerDetentIdentifierLarge]; + } + // FIXME: This should be handled with conversion rather than straightforward usage + if (withDefault.modal.selectedDetentIdentifier.hasValue) { + //[sheet setSelectedDetentIdentifier:[RNNConvert UISheetPresentationControllerDetentIdentifier:[withDefault.modal.selectedDetentIdentifier withDefault:nil]]]; + [sheet setSelectedDetentIdentifier:UISheetPresentationControllerDetentIdentifierMedium]; + } + if (withDefault.modal.preferredCornerRadius.hasValue) { + [sheet setPreferredCornerRadius:[withDefault.modal.preferredCornerRadius.get doubleValue]]; + } + } + } + newVc.waitForRender = [withDefault.animations.showModal.enter shouldWaitForRender]; newVc.modalPresentationStyle = [RNNConvert UIModalPresentationStyle:[withDefault.modalPresentationStyle withDefault:@"default"]]; diff --git a/lib/ios/RNNConvert.h b/lib/ios/RNNConvert.h index b21c73b042d..3fbf16ff567 100644 --- a/lib/ios/RNNConvert.h +++ b/lib/ios/RNNConvert.h @@ -7,4 +7,6 @@ + (UIModalTransitionStyle)UIModalTransitionStyle:(id)json; ++ (UISheetPresentationControllerDetentIdentifier)UISheetPresentationControllerDetentIdentifier:(id)json; + @end diff --git a/lib/ios/RNNConvert.m b/lib/ios/RNNConvert.m index 873042dec5e..15b32768d9b 100644 --- a/lib/ios/RNNConvert.m +++ b/lib/ios/RNNConvert.m @@ -32,4 +32,11 @@ + (UIModalPresentationStyle)defaultModalPresentationStyle { }), UIModalPresentationFullScreen, integerValue) +//FIXME: The conversion is incorrect +//RCT_ENUM_CONVERTER(UISheetPresentationControllerDetentIdentifier, (@{ +// @"medium" : UISheetPresentationControllerDetentIdentifierMedium, +// @"large" : UISheetPresentationControllerDetentIdentifierLarge +// }), +// nil, integerValue) + @end diff --git a/lib/ios/RNNModalOptions.h b/lib/ios/RNNModalOptions.h index 940c3305b43..893b0df1413 100644 --- a/lib/ios/RNNModalOptions.h +++ b/lib/ios/RNNModalOptions.h @@ -3,5 +3,13 @@ @interface RNNModalOptions : RNNOptions @property(nonatomic, strong) Bool *swipeToDismiss; +@property(nonatomic, strong) Bool *prefersScrollingExpandsWhenScrolledToEdge; +@property(nonatomic, strong) Bool *prefersEdgeAttachedInCompactHeight; +@property(nonatomic, strong) Bool *widthFollowsPreferredContentSizeWhenEdgeAttached; +@property(nonatomic, strong) Bool *prefersGrabberVisible; +@property(nonatomic, strong) Number *preferredCornerRadius; +@property(nonatomic, strong) NSArray *detents; +@property(nonatomic, strong) Text *largestUndimmedDetent; +@property(nonatomic, strong) Text *selectedDetentIdentifier; @end diff --git a/lib/ios/RNNModalOptions.m b/lib/ios/RNNModalOptions.m index cc795cc82c5..14559b24e2e 100644 --- a/lib/ios/RNNModalOptions.m +++ b/lib/ios/RNNModalOptions.m @@ -1,16 +1,45 @@ #import "RNNModalOptions.h" +#import "OptionsArrayParser.h" @implementation RNNModalOptions - (instancetype)initWithDict:(NSDictionary *)dict { self = [super initWithDict:dict]; self.swipeToDismiss = [BoolParser parse:dict key:@"swipeToDismiss"]; + self.prefersScrollingExpandsWhenScrolledToEdge = [BoolParser parse:dict key:@"prefersScrollingExpandsWhenScrolledToEdge"]; + self.prefersEdgeAttachedInCompactHeight = [BoolParser parse:dict key:@"prefersEdgeAttachedInCompactHeight"]; + self.widthFollowsPreferredContentSizeWhenEdgeAttached = [BoolParser parse:dict key:@"widthFollowsPreferredContentSizeWhenEdgeAttached"]; + self.prefersGrabberVisible = [BoolParser parse:dict key:@"prefersGrabberVisible"]; + self.preferredCornerRadius = [NumberParser parse:dict key:@"preferredCornerRadius"]; + self.largestUndimmedDetent = [TextParser parse:dict key:@"largestUndimmedDetent"]; + self.selectedDetentIdentifier = [TextParser parse:dict key:@"selectedDetentIdentifier"]; + //FIXME: Probably incorrect usage of the parser and incorrect class + self.detents = [OptionsArrayParser parse:dict key:@"detents" ofClass:Text.class]; return self; } - (void)mergeOptions:(RNNModalOptions *)options { if (options.swipeToDismiss.hasValue) self.swipeToDismiss = options.swipeToDismiss; + if (options.prefersScrollingExpandsWhenScrolledToEdge.hasValue) { + self.prefersScrollingExpandsWhenScrolledToEdge = options.prefersScrollingExpandsWhenScrolledToEdge; + } + if (options.prefersEdgeAttachedInCompactHeight.hasValue) + self.prefersEdgeAttachedInCompactHeight = options.prefersEdgeAttachedInCompactHeight; + if (options.widthFollowsPreferredContentSizeWhenEdgeAttached.hasValue) { + self.widthFollowsPreferredContentSizeWhenEdgeAttached = options.widthFollowsPreferredContentSizeWhenEdgeAttached; + } + if (options.prefersGrabberVisible.hasValue) + self.prefersGrabberVisible = options.prefersGrabberVisible; + if (options.preferredCornerRadius.hasValue) + self.preferredCornerRadius = options.preferredCornerRadius; + if (options.largestUndimmedDetent.hasValue) + self.largestUndimmedDetent = options.largestUndimmedDetent; + if (options.selectedDetentIdentifier.hasValue) + self.selectedDetentIdentifier = options.selectedDetentIdentifier; + if (options.detents) + self.detents = options.detents; + } @end diff --git a/lib/src/interfaces/Options.ts b/lib/src/interfaces/Options.ts index cb8997eab88..f3c58572965 100644 --- a/lib/src/interfaces/Options.ts +++ b/lib/src/interfaces/Options.ts @@ -1086,12 +1086,66 @@ export interface OverlayOptions { handleKeyboardEvents?: boolean; } +export enum ModalDetent { + medium = "medium", + large = "large", +} + export interface ModalOptions { /** * Control whether this modal should be dismiss using swipe gesture when the modalPresentationStyle = 'pageSheet' * #### (iOS specific) */ swipeToDismiss?: boolean; + + /** + * The array of heights where a sheet can rest. + * #### (iOS 15+ specific) + */ + detents?: ModalDetent[], + + /** + * The largest detent that doesn’t dim the view underneath the sheet. + * #### (iOS 15+ specific) + */ + largestUndimmedDetent?: ModalDetent, + + /** + * The identifier of the most recently selected detent. + * #### (iOS 15+ specific) + * */ + selectedDetentIdentifier?: ModalDetent, + + /** + * A boolean value that determines whether scrolling expands the sheet to a larger detent. + * After the sheet reaches its largest detent, scrolling begins. + * #### (iOS 15+ specific) + */ + prefersScrollingExpandsWhenScrolledToEdge?: boolean, + + /** + * A boolean value that determines whether the sheet attaches to the bottom edge of the screen in a compact-height size class. + * #### (iOS 15+ specific) + */ + prefersEdgeAttachedInCompactHeight?: boolean, + + /** + * A boolean value that determines whether the sheet's width matches its view controller's preferred content size. + * #### (iOS 15+ specific) + */ + widthFollowsPreferredContentSizeWhenEdgeAttached?: boolean, + + /** + * The corner radius that the sheet attempts to present with. + * #### (iOS 15+ specific) + */ + preferredCornerRadius?: number; + + /** + * A boolean value that determines whether the sheet shows a grabber at the top. + * #### (iOS 15+ specific) + */ + prefersGrabberVisible?: boolean; } export interface OptionsPreviewAction { diff --git a/playground/src/screens/NavigationScreen.tsx b/playground/src/screens/NavigationScreen.tsx index eaa893930a6..4aad516d13e 100644 --- a/playground/src/screens/NavigationScreen.tsx +++ b/playground/src/screens/NavigationScreen.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Platform } from 'react-native'; -import { NavigationComponentProps, OptionsModalPresentationStyle } from 'react-native-navigation'; +import { ModalDetent, NavigationComponentProps, OptionsModalPresentationStyle } from 'react-native-navigation'; import Root from '../components/Root'; import Button from '../components/Button'; import Navigation from './../services/Navigation'; @@ -43,6 +43,7 @@ export default class NavigationScreen extends React.Component {