From 2bb1c263dbdce8f92ed5af41a729470238157799 Mon Sep 17 00:00:00 2001 From: Jesse Sessler Date: Thu, 28 Apr 2016 15:59:11 -0700 Subject: [PATCH] Modal Animation Types Summary: Currently the Modal component uses the slide up / down animation for presenting and hiding the Modal with no options. This PR gives users a choice to use a fade in / out animation or the current slide animation (slide is the default). Android and iOS. ![](http://g.recordit.co/nfJSg487Ox.gif) ![](http://g.recordit.co/QHGDuUFbPy.gif) I've updated the UIExplorer and documentation. ![image](https://cloud.githubusercontent.com/assets/4265163/14743130/0bd8282c-086e-11e6-93eb-3d344431337d.png) Thanks! Closes https://github.com/facebook/react-native/pull/7156 Differential Revision: D3237809 Pulled By: javache fb-gh-sync-id: 813e56ada8b19990dc5018527dc3a81b2c8b349a fbshipit-source-id: 813e56ada8b19990dc5018527dc3a81b2c8b349a --- Examples/UIExplorer/ModalExample.js | 26 +++++++++++++------ Libraries/Modal/Modal.js | 20 +++++++++++--- React/Views/RCTModalHostView.h | 2 +- React/Views/RCTModalHostView.m | 15 +++++++++-- React/Views/RCTModalHostViewManager.m | 2 +- .../views/modal/ReactModalHostManager.java | 6 ++--- .../react/views/modal/ReactModalHostView.java | 12 +++++---- .../src/main/res/views/modal/anim/fade_in.xml | 7 +++++ .../main/res/views/modal/anim/fade_out.xml | 7 +++++ .../main/res/views/modal/values/themes.xml | 17 +++++++++--- 10 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 ReactAndroid/src/main/res/views/modal/anim/fade_in.xml create mode 100644 ReactAndroid/src/main/res/views/modal/anim/fade_out.xml diff --git a/Examples/UIExplorer/ModalExample.js b/Examples/UIExplorer/ModalExample.js index 5d495206e..1717fb0e0 100644 --- a/Examples/UIExplorer/ModalExample.js +++ b/Examples/UIExplorer/ModalExample.js @@ -66,7 +66,7 @@ var Button = React.createClass({ var ModalExample = React.createClass({ getInitialState() { return { - animated: true, + animationType: 'none', modalVisible: false, transparent: false, }; @@ -76,8 +76,8 @@ var ModalExample = React.createClass({ this.setState({modalVisible: visible}); }, - _toggleAnimated() { - this.setState({animated: !this.state.animated}); + _setAnimationType(type) { + this.setState({animationType: type}); }, _toggleTransparent() { @@ -91,18 +91,21 @@ var ModalExample = React.createClass({ var innerContainerTransparentStyle = this.state.transparent ? {backgroundColor: '#fff', padding: 20} : null; + var activeButtonStyle = { + backgroundColor: '#ddd' + }; return ( {this._setModalVisible(false)}} > - This modal was presented {this.state.animated ? 'with' : 'without'} animation. + This modal was presented {this.state.animationType === 'none' ? 'without' : 'with'} animation. + + diff --git a/Libraries/Modal/Modal.js b/Libraries/Modal/Modal.js index 1451caf86..8dea02528 100644 --- a/Libraries/Modal/Modal.js +++ b/Libraries/Modal/Modal.js @@ -16,6 +16,7 @@ const PropTypes = require('ReactPropTypes'); const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); +const deprecatedPropType = require('deprecatedPropType'); const requireNativeComponent = require('requireNativeComponent'); const RCTModalHostView = requireNativeComponent('RCTModalHostView', null); @@ -35,7 +36,11 @@ const RCTModalHostView = requireNativeComponent('RCTModalHostView', null); */ class Modal extends React.Component { static propTypes = { - animated: PropTypes.bool, + animated: deprecatedPropType( + PropTypes.bool, + 'Use the `animationType` prop instead.' + ), + animationType: PropTypes.oneOf(['none', 'slide', 'fade']), transparent: PropTypes.bool, visible: PropTypes.bool, onRequestClose: Platform.OS === 'android' ? PropTypes.func.isRequired : PropTypes.func, @@ -55,9 +60,18 @@ class Modal extends React.Component { backgroundColor: this.props.transparent ? 'transparent' : 'white', }; + let animationType = this.props.animationType; + if (!animationType) { + // manually setting default prop here to keep support for the deprecated 'animated' prop + animationType = 'none'; + if (this.props.animated) { + animationType = 'slide'; + } + } + return ( -@property (nonatomic, assign, getter=isAnimated) BOOL animated; +@property (nonatomic, copy) NSString *animationType; @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 b5908d0ab..c31402493 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -82,7 +82,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder) - (void)dismissModalViewController { if (_isPresented) { - [_modalViewController dismissViewControllerAnimated:self.animated completion:nil]; + [_modalViewController dismissViewControllerAnimated:[self hasAnimationType] completion:nil]; _isPresented = NO; } } @@ -93,7 +93,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder) if (!_isPresented && self.window) { RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller"); - [self.reactViewController presentViewController:_modalViewController animated:self.animated completion:^{ + + if ([self.animationType isEqualToString:@"fade"]) { + _modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + } else if ([self.animationType isEqualToString:@"slide"]) { + _modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + } + [self.reactViewController presentViewController:_modalViewController animated:[self hasAnimationType] completion:^{ if (_onShow) { _onShow(nil); } @@ -123,6 +129,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder) return _modalViewController.modalPresentationStyle == UIModalPresentationCustom; } +- (BOOL)hasAnimationType +{ + return ![self.animationType isEqualToString:@"none"]; +} + - (void)setTransparent:(BOOL)transparent { _modalViewController.modalPresentationStyle = transparent ? UIModalPresentationCustom : UIModalPresentationFullScreen; diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 8a370556e..b26a91206 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -62,7 +62,7 @@ RCT_EXPORT_MODULE() [_hostViews removeAllObjects]; } -RCT_EXPORT_VIEW_PROPERTY(animated, BOOL) +RCT_EXPORT_VIEW_PROPERTY(animationType, NSString) RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL) RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index e37da05b7..c8091251e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -55,9 +55,9 @@ public class ReactModalHostManager extends ViewGroupManager view.dismiss(); } - @ReactProp(name = "animated") - public void setAnimated(ReactModalHostView view, boolean animated) { - view.setAnimated(animated); + @ReactProp(name = "animationType") + public void setAnimationType(ReactModalHostView view, String animationType) { + view.setAnimationType(animationType); } @ReactProp(name = "transparent") diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index 17d583c19..c15b493fc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -56,7 +56,7 @@ public class ReactModalHostView extends ViewGroup { private DialogRootViewGroup mHostView; private @Nullable Dialog mDialog; private boolean mTransparent; - private boolean mAnimated; + private String mAnimationType; // Set this flag to true if changing a particular property on the view requires a new Dialog to // be created. For instance, animation does since it affects Dialog creation through the theme // but transparency does not since we can access the window to update the property. @@ -131,8 +131,8 @@ public class ReactModalHostView extends ViewGroup { mTransparent = transparent; } - protected void setAnimated(boolean animated) { - mAnimated = animated; + protected void setAnimationType(String animationType) { + mAnimationType = animationType; mPropertyRequiresNewDialog = true; } @@ -162,8 +162,10 @@ public class ReactModalHostView extends ViewGroup { // Reset the flag since we are going to create a new dialog mPropertyRequiresNewDialog = false; int theme = R.style.Theme_FullScreenDialog; - if (mAnimated) { - theme = R.style.Theme_FullScreenDialogAnimated; + if (mAnimationType.equals("fade")) { + theme = R.style.Theme_FullScreenDialogAnimatedFade; + } else if (mAnimationType.equals("slide")) { + theme = R.style.Theme_FullScreenDialogAnimatedSlide; } mDialog = new Dialog(getContext(), theme); diff --git a/ReactAndroid/src/main/res/views/modal/anim/fade_in.xml b/ReactAndroid/src/main/res/views/modal/anim/fade_in.xml new file mode 100644 index 000000000..7fe329fdc --- /dev/null +++ b/ReactAndroid/src/main/res/views/modal/anim/fade_in.xml @@ -0,0 +1,7 @@ + + diff --git a/ReactAndroid/src/main/res/views/modal/anim/fade_out.xml b/ReactAndroid/src/main/res/views/modal/anim/fade_out.xml new file mode 100644 index 000000000..4919eda8d --- /dev/null +++ b/ReactAndroid/src/main/res/views/modal/anim/fade_out.xml @@ -0,0 +1,7 @@ + + diff --git a/ReactAndroid/src/main/res/views/modal/values/themes.xml b/ReactAndroid/src/main/res/views/modal/values/themes.xml index 67ce8a502..0efa67c76 100644 --- a/ReactAndroid/src/main/res/views/modal/values/themes.xml +++ b/ReactAndroid/src/main/res/views/modal/values/themes.xml @@ -8,13 +8,24 @@ @android:color/transparent - - + + + + + +