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
This commit is contained in:
parent
b5f14ea8f1
commit
2bb1c263db
|
@ -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 (
|
||||
<View>
|
||||
<Modal
|
||||
animated={this.state.animated}
|
||||
animationType={this.state.animationType}
|
||||
transparent={this.state.transparent}
|
||||
visible={this.state.modalVisible}
|
||||
onRequestClose={() => {this._setModalVisible(false)}}
|
||||
>
|
||||
<View style={[styles.container, modalBackgroundStyle]}>
|
||||
<View style={[styles.innerContainer, innerContainerTransparentStyle]}>
|
||||
<Text>This modal was presented {this.state.animated ? 'with' : 'without'} animation.</Text>
|
||||
<Text>This modal was presented {this.state.animationType === 'none' ? 'without' : 'with'} animation.</Text>
|
||||
<Button
|
||||
onPress={this._setModalVisible.bind(this, false)}
|
||||
style={styles.modalButton}>
|
||||
|
@ -111,10 +114,17 @@ var ModalExample = React.createClass({
|
|||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitle}>Animated</Text>
|
||||
<Switch value={this.state.animated} onValueChange={this._toggleAnimated} />
|
||||
<Text style={styles.rowTitle}>Animation Type</Text>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'none')} style={this.state.animationType === 'none' ? activeButtonStyle : {}}>
|
||||
none
|
||||
</Button>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'slide')} style={this.state.animationType === 'slide' ? activeButtonStyle : {}}>
|
||||
slide
|
||||
</Button>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'fade')} style={this.state.animationType === 'fade' ? activeButtonStyle : {}}>
|
||||
fade
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<View style={styles.row}>
|
||||
|
|
|
@ -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 (
|
||||
<RCTModalHostView
|
||||
animated={this.props.animated}
|
||||
animationType={animationType}
|
||||
transparent={this.props.transparent}
|
||||
onRequestClose={this.props.onRequestClose}
|
||||
onShow={this.props.onShow}
|
||||
|
@ -88,4 +102,4 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
module.exports = Modal;
|
||||
module.exports = Modal;
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
@interface RCTModalHostView : UIView <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, assign, getter=isAnimated) BOOL animated;
|
||||
@property (nonatomic, copy) NSString *animationType;
|
||||
@property (nonatomic, assign, getter=isTransparent) BOOL transparent;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onShow;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ public class ReactModalHostManager extends ViewGroupManager<ReactModalHostView>
|
|||
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")
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
/>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"
|
||||
/>
|
|
@ -8,13 +8,24 @@
|
|||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.FullScreenDialogAnimated" parent="Theme.FullScreenDialog">
|
||||
<item name="android:windowAnimationStyle">@style/DialogAnimation</item>
|
||||
<style name="Theme.FullScreenDialogAnimatedSlide" parent="Theme.FullScreenDialog">
|
||||
<item name="android:windowAnimationStyle">@style/DialogAnimationSlide</item>
|
||||
</style>
|
||||
|
||||
<style name="DialogAnimation">
|
||||
<style name="Theme.FullScreenDialogAnimatedFade" parent="Theme.FullScreenDialog">
|
||||
<item name="android:windowAnimationStyle">@style/DialogAnimationFade</item>
|
||||
</style>
|
||||
|
||||
<style name="DialogAnimationSlide">
|
||||
<item name="android:windowEnterAnimation">@anim/slide_up</item>
|
||||
<item name="android:windowExitAnimation">@anim/slide_down</item>
|
||||
</style>
|
||||
|
||||
<style name="DialogAnimationFade">
|
||||
<item name="android:windowEnterAnimation">@anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/fade_out</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue