diff --git a/Libraries/Modal/Modal.js b/Libraries/Modal/Modal.js index 2243da9d70..b0cbf4d095 100644 --- a/Libraries/Modal/Modal.js +++ b/Libraries/Modal/Modal.js @@ -78,6 +78,7 @@ const RCTModalHostView = requireNativeComponent('RCTModalHostView', null); * } * ``` */ + class Modal extends React.Component { static propTypes = { /** @@ -90,6 +91,19 @@ class Modal extends React.Component { * Default is set to `none`. */ animationType: PropTypes.oneOf(['none', 'slide', 'fade']), + /** + * The `presentationStyle` prop controls how the modal appears (generally on larger devices such as iPad or plus-sized iPhones). + * See https://developer.apple.com/reference/uikit/uimodalpresentationstyle for details. + * @platform ios + * + * - `fullScreen` covers the screen completely + * - `pageSheet` covers portrait-width view centered (only on larger devices) + * - `formSheet` covers narrow-width view centered (only on larger devices) + * - `overFullScreen` covers the screen completely, but allows transparency + * + * Default is set to `overFullScreen` or `fullScreen` depending on `transparent` property. + */ + presentationStyle: PropTypes.oneOf(['fullScreen', 'pageSheet', 'formSheet', 'overFullScreen']), /** * The `transparent` prop determines whether your modal will fill the entire view. Setting this to `true` will render the modal over a transparent background. */ @@ -119,6 +133,7 @@ class Modal extends React.Component { /** * The `supportedOrientations` prop allows the modal to be rotated to any of the specified orientations. * On iOS, the modal is still restricted by what's specified in your app's Info.plist's UISupportedInterfaceOrientations field. + * When using `presentationStyle` of `pageSheet` or `formSheet`, this property will be ignored by iOS. * @platform ios */ supportedOrientations: PropTypes.arrayOf(PropTypes.oneOf(['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right'])), @@ -139,6 +154,21 @@ class Modal extends React.Component { rootTag: PropTypes.number, }; + constructor(props: Object) { + super(props); + Modal._confirmProps(props); + } + + componentWillReceiveProps(nextProps: Object) { + Modal._confirmProps(nextProps); + } + + static _confirmProps(props: Object) { + if (props.presentationStyle && props.presentationStyle !== 'overFullScreen' && props.transparent) { + console.warn(`Modal with '${props.presentationStyle}' presentation style and 'transparent' value is not supported.`); + } + } + render(): ?React.Element { if (this.props.visible === false) { return null; @@ -157,6 +187,14 @@ class Modal extends React.Component { } } + let presentationStyle = this.props.presentationStyle; + if (!presentationStyle) { + presentationStyle = 'fullScreen'; + if (this.props.transparent) { + presentationStyle = 'overFullScreen'; + } + } + const innerChildren = __DEV__ ? ( {this.props.children} @@ -166,6 +204,7 @@ class Modal extends React.Component { return ( this._setModalVisible(false)} @@ -141,6 +143,21 @@ class ModalExample extends React.Component { + + Presentation style + this.setState({presentationStyle})} + itemStyle={styles.pickerItem} + > + + + + + + + + Supported orientations @property (nonatomic, copy) NSString *animationType; +@property (nonatomic, assign) UIModalPresentationStyle presentationStyle; @property (nonatomic, assign, getter=isTransparent) BOOL transparent; @property (nonatomic, copy) RCTDirectEventBlock onShow; diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index 687352e162..2b8f7c84f9 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -132,6 +132,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder) } else if ([self.animationType isEqualToString:@"slide"]) { _modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; } + if (self.presentationStyle != UIModalPresentationNone) { + _modalViewController.modalPresentationStyle = self.presentationStyle; + } [_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]]; _isPresented = YES; } @@ -165,6 +168,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder) - (void)setTransparent:(BOOL)transparent { + if (self.isTransparent != transparent) { + return; + } + _modalViewController.modalPresentationStyle = transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen; } diff --git a/React/Views/RCTModalHostViewManager.h b/React/Views/RCTModalHostViewManager.h index 2931e05472..edf6daba77 100644 --- a/React/Views/RCTModalHostViewManager.h +++ b/React/Views/RCTModalHostViewManager.h @@ -9,6 +9,13 @@ #import #import +#import + +@interface RCTConvert (RCTModalHostView) + ++ (UIModalPresentationStyle)UIModalPresentationStyle:(id)json; + +@end typedef void (^RCTModalViewInteractionBlock)(UIViewController *reactViewController, UIViewController *viewController, BOOL animated, dispatch_block_t completionBlock); diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 31821af33f..a5adde4f00 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -15,6 +15,19 @@ #import "RCTShadowView.h" #import "RCTUtils.h" +@implementation RCTConvert (RCTModalHostView) + +RCT_ENUM_CONVERTER(UIModalPresentationStyle, (@{ + @"fullScreen": @(UIModalPresentationFullScreen), +#if !TARGET_OS_TV + @"pageSheet": @(UIModalPresentationPageSheet), + @"formSheet": @(UIModalPresentationFormSheet), +#endif + @"overFullScreen": @(UIModalPresentationOverFullScreen), +}), UIModalPresentationFullScreen, integerValue) + +@end + @interface RCTModalHostShadowView : RCTShadowView @end @@ -91,6 +104,7 @@ RCT_EXPORT_MODULE() } RCT_EXPORT_VIEW_PROPERTY(animationType, NSString) +RCT_EXPORT_VIEW_PROPERTY(presentationStyle, UIModalPresentationStyle) RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL) RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)