Skip to content

Commit 0f14933

Browse files
author
Alexsander Akers
committed
Enable transparent modal presentation with <Modal />
Summary: Enable transparent modal backgrounds using `UIModalPresentationCustom` modal presentation style.
1 parent dc01ecb commit 0f14933

6 files changed

Lines changed: 166 additions & 35 deletions

File tree

Examples/UIExplorer/ModalExample.js

Lines changed: 103 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var React = require('react-native');
1919
var {
2020
Modal,
2121
StyleSheet,
22+
SwitchIOS,
2223
Text,
2324
TouchableHighlight,
2425
View,
@@ -29,53 +30,98 @@ exports.framework = 'React';
2930
exports.title = '<Modal>';
3031
exports.description = 'Component for presenting modal views.';
3132

33+
var Button = React.createClass({
34+
getInitialState() {
35+
return {
36+
active: false,
37+
};
38+
},
39+
40+
_onHighlight() {
41+
this.setState({active: true});
42+
},
43+
44+
_onUnhighlight() {
45+
this.setState({active: false});
46+
},
47+
48+
render() {
49+
var colorStyle = {
50+
color: this.state.active ? '#fff' : '#000',
51+
};
52+
return (
53+
<TouchableHighlight
54+
onHideUnderlay={this._onUnhighlight}
55+
onPress={this.props.onPress}
56+
onShowUnderlay={this._onHighlight}
57+
style={[styles.button, this.props.style]}
58+
underlayColor="#a9d9d4">
59+
<Text style={[styles.buttonText, colorStyle]}>{this.props.children}</Text>
60+
</TouchableHighlight>
61+
);
62+
}
63+
});
64+
3265
var ModalExample = React.createClass({
33-
getInitialState: function() {
66+
getInitialState() {
3467
return {
35-
openModal: null,
68+
animated: true,
69+
modalVisible: false,
70+
transparent: false,
3671
};
3772
},
3873

39-
_closeModal: function() {
40-
this.setState({openModal: null});
74+
_setModalVisible(visible) {
75+
this.setState({modalVisible: visible});
4176
},
4277

43-
_openAnimatedModal: function() {
44-
this.setState({openModal: 'animated'});
78+
_toggleAnimated() {
79+
this.setState({animated: !this.state.animated});
4580
},
4681

47-
_openNotAnimatedModal: function() {
48-
this.setState({openModal: 'not-animated'});
82+
_toggleTransparent() {
83+
this.setState({transparent: !this.state.transparent});
4984
},
5085

51-
render: function() {
86+
render() {
87+
var modalBackgroundStyle = {
88+
backgroundColor: this.state.transparent ? 'rgba(0, 0, 0, 0.5)' : '#f5fcff',
89+
};
90+
var innerContainerTransparentStyle = this.state.transparent
91+
? {backgroundColor: '#fff', padding: 20}
92+
: null;
93+
5294
return (
5395
<View>
54-
<Modal animated={true} visible={this.state.openModal === 'animated'}>
55-
<View style={styles.container}>
56-
<Text>This modal was presented with animation.</Text>
57-
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
58-
<Text>Close</Text>
59-
</TouchableHighlight>
96+
<Modal
97+
animated={this.state.animated}
98+
transparent={this.state.transparent}
99+
visible={this.state.modalVisible}>
100+
<View style={[styles.container, modalBackgroundStyle]}>
101+
<View style={[styles.innerContainer, innerContainerTransparentStyle]}>
102+
<Text>This modal was presented {this.state.animated ? 'with' : 'without'} animation.</Text>
103+
<Button
104+
onPress={this._setModalVisible.bind(this, false)}
105+
style={styles.modalButton}>
106+
Close
107+
</Button>
108+
</View>
60109
</View>
61110
</Modal>
62111

63-
<Modal visible={this.state.openModal === 'not-animated'}>
64-
<View style={styles.container}>
65-
<Text>This modal was presented immediately, without animation.</Text>
66-
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._closeModal}>
67-
<Text>Close</Text>
68-
</TouchableHighlight>
69-
</View>
70-
</Modal>
112+
<View style={styles.row}>
113+
<Text style={styles.rowTitle}>Animated</Text>
114+
<SwitchIOS value={this.state.animated} onValueChange={this._toggleAnimated} />
115+
</View>
71116

72-
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openAnimatedModal}>
73-
<Text>Present Animated</Text>
74-
</TouchableHighlight>
117+
<View style={styles.row}>
118+
<Text style={styles.rowTitle}>Transparent</Text>
119+
<SwitchIOS value={this.state.transparent} onValueChange={this._toggleTransparent} />
120+
</View>
75121

76-
<TouchableHighlight underlayColor="#a9d9d4" onPress={this._openNotAnimatedModal}>
77-
<Text>Present Without Animation</Text>
78-
</TouchableHighlight>
122+
<Button onPress={this._setModalVisible.bind(this, true)}>
123+
Present
124+
</Button>
79125
</View>
80126
);
81127
},
@@ -91,9 +137,36 @@ exports.examples = [
91137

92138
var styles = StyleSheet.create({
93139
container: {
140+
flex: 1,
141+
justifyContent: 'center',
142+
padding: 20,
143+
},
144+
innerContainer: {
145+
borderRadius: 10,
146+
},
147+
row: {
94148
alignItems: 'center',
95-
backgroundColor: '#f5fcff',
96149
flex: 1,
150+
flexDirection: 'row',
151+
marginBottom: 20,
152+
},
153+
rowTitle: {
154+
flex: 1,
155+
fontWeight: 'bold',
156+
},
157+
button: {
158+
borderRadius: 5,
159+
flex: 1,
160+
height: 44,
97161
justifyContent: 'center',
162+
overflow: 'hidden',
163+
},
164+
buttonText: {
165+
fontSize: 18,
166+
margin: 5,
167+
textAlign: 'center',
168+
},
169+
modalButton: {
170+
marginTop: 10,
98171
},
99172
});

Libraries/Modal/Modal.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212
'use strict';
1313

14+
var PropTypes = require('ReactPropTypes');
1415
var React = require('React');
1516
var StyleSheet = require('StyleSheet');
1617
var View = require('View');
@@ -24,16 +25,28 @@ class Modal extends React.Component {
2425
return null;
2526
}
2627

28+
if (this.props.transparent) {
29+
var containerBackgroundColor = {backgroundColor: 'transparent'};
30+
}
31+
2732
return (
28-
<RCTModalHostView animated={this.props.animated} style={styles.modal}>
29-
<View style={styles.container}>
33+
<RCTModalHostView
34+
animated={this.props.animated}
35+
transparent={this.props.transparent}
36+
style={styles.modal}>
37+
<View style={[styles.container, containerBackgroundColor]}>
3038
{this.props.children}
3139
</View>
3240
</RCTModalHostView>
3341
);
3442
}
3543
}
3644

45+
Modal.propTypes = {
46+
animated: PropTypes.bool,
47+
transparent: PropTypes.bool,
48+
};
49+
3750
var styles = StyleSheet.create({
3851
modal: {
3952
position: 'absolute',

React/Views/RCTModalHostView.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99

1010
#import <UIKit/UIKit.h>
1111

12+
#import "RCTInvalidating.h"
13+
1214
@class RCTBridge;
1315

14-
@interface RCTModalHostView : UIView
16+
@interface RCTModalHostView : UIView <RCTInvalidating>
1517

1618
@property (nonatomic, assign, getter=isAnimated) BOOL animated;
19+
@property (nonatomic, assign, getter=isTransparent) BOOL transparent;
1720

1821
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
1922

React/Views/RCTModalHostView.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,21 @@ - (void)didMoveToSuperview
8181
}
8282
}
8383

84+
- (void)invalidate
85+
{
86+
dispatch_async(dispatch_get_main_queue(), ^{
87+
[_modalViewController dismissViewControllerAnimated:self.animated completion:nil];
88+
});
89+
}
90+
91+
- (BOOL)isTransparent
92+
{
93+
return _modalViewController.modalPresentationStyle == UIModalPresentationCustom;
94+
}
95+
96+
- (void)setTransparent:(BOOL)transparent
97+
{
98+
_modalViewController.modalPresentationStyle = transparent ? UIModalPresentationCustom : UIModalPresentationFullScreen;
99+
}
100+
84101
@end

React/Views/RCTModalHostViewManager.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#import "RCTViewManager.h"
1111

12-
@interface RCTModalHostViewManager : RCTViewManager
12+
#import "RCTInvalidating.h"
13+
14+
@interface RCTModalHostViewManager : RCTViewManager <RCTInvalidating>
1315

1416
@end

React/Views/RCTModalHostViewManager.m

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,37 @@
1414
#import "RCTTouchHandler.h"
1515

1616
@implementation RCTModalHostViewManager
17+
{
18+
NSHashTable *_hostViews;
19+
}
1720

1821
RCT_EXPORT_MODULE()
1922

23+
- (instancetype)init
24+
{
25+
if ((self = [super init])) {
26+
_hostViews = [NSHashTable weakObjectsHashTable];
27+
}
28+
29+
return self;
30+
}
31+
2032
- (UIView *)view
2133
{
22-
return [[RCTModalHostView alloc] initWithBridge:self.bridge];
34+
UIView *view = [[RCTModalHostView alloc] initWithBridge:self.bridge];
35+
[_hostViews addObject:view];
36+
return view;
37+
}
38+
39+
- (void)invalidate
40+
{
41+
for (RCTModalHostView *hostView in _hostViews) {
42+
[hostView invalidate];
43+
}
44+
[_hostViews removeAllObjects];
2345
}
2446

2547
RCT_EXPORT_VIEW_PROPERTY(animated, BOOL)
48+
RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL)
2649

2750
@end

0 commit comments

Comments
 (0)