Closed Doko-Demo-Doa closed 7 years ago
Hey!
It should be possible by toying a bit with the touchable options of this View.
Would you mind giving it a try and submitting a PR?
Otherwise I'll take a look at it in my spare time :)
😆 pls add this function~~ thanks
Thank you @mmazzarolo , actually I fixed using exactly same method you mentioned. It works, but since the first image in your preview images (on README.md) does it correctly, I thought it was implemented already, wasn't it?
@Doko-Demo-Doa according to the source code, haven't implemented yet, there is no close event on backdrop view or you can do sth like this, not so good but should be work.
<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
<Modal isVisible={this.state.visible} ...>
<TouchableWithoutFeedback onPress={() => {}}>
... your custom view
</TouchableWithoutFeedback
</Modal>
</TouchableWithoutFeedback>
@Doko-Demo-Doa oh wow, honestly I don't remember how I implemented it for that screenshot (I did it in a customized version of this lib 😞 ).
@DavidKongDesheng the problem with that solution is that clicking everywhere on the modal will close it.
In my opinion the right solution would be using onStartShouldSetResponderCapture
(or something similar in the right place.
The perfect solution would be that:
onClose
or onBackdropPress
);What do you think guys?
Also, thank you @Doko-Demo-Doa and @DavidKongDesheng for the discussion, any feedback is of course warmly welcomed! 😸
@Doko-Demo-Doa could you share with us your solution?
I played a bit with it.
It seems that the right way to handle it is by applying something like this:
onStartShouldSetResponder={() => {
console.log('You pressed the backdrop!');
return true;
}}
to the backdrop view.
The problem with this approach is that it won't work correctly if you're centering the modal content using this:
content: {
flex: 1,
justifyContent: 'center'
}
...which is the default style applied to the content.
I guess we should wait until react-native 0.43 stable lands and try changing that style to the new margin: auto
, which I hope will make the backdrop press work correctly.
I'm obviously open to discussions on the matter :)
@mmazzarolo On my solution I added two touch events, one for Modal itself which will close the modal, the other one is on the Modal content view which will do nothing 😸, so only when you click the backdrop will close the modal. <TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}> <Modal isVisible={this.state.visible} ...> <TouchableWithoutFeedback onPress={() => {}}> ... your custom view </TouchableWithoutFeedback
Yup, the right solution should be make the backdrop respond to user touch.
Oh, nice! @DavidKongDesheng did you test your solution? Do you think it works correctly in most cases? Would you mind pasting here your complete snippet? Thanks!
@mmazzarolo Just added touch event to your example code, you can download and try, but this is not the best way to implement. app.js.zip
It seems that RN's modal component is pretty meh. If there's something like Dialog on Android or UIAlertView on iOS, it would be much better in this case :(
Hi,
I did exactly the same thing as @DavidKongDesheng but still don't working on my side !
<TouchableWithoutFeedback onPress={() => this.setState({ modalVisible: false })}>
<Modal transparent={true} ref="modal" visible={this.state.modalVisible} onRequestClose={this.close.bind(this)} animationType={this.state.animationType}>
{this.renderOptionList()}
</Modal>
</TouchableWithoutFeedback>
Or maybe i don't make it well... no ?
react-native 0.42.3
Hi! For me the solution of @DavidKongDesheng works great 🔥 This is my code:
` constructor(props) { super(props); this.state = { isModalVisible: false } }
_showModal() { this.setState({ isModalVisible: true }) }
_hideModal() { this.setState({ isModalVisible: false }) }
render() { const { alerts, showAlert } = this.props; return (
@emaLorenzo Thanks! But it doesn't work :(
I don't know why it doesn't triggered the background :/
@warka0 Can't reproduce it according the attached codes, but it looks like you are using Modal from 'react-native' not from 'react-native-modal'
Or maybe try
Or more codes attach...
@emaLorenzo Thanks for your solution, this worked for me 👏 👍
@DavidKongDesheng Your solution worked for me, thanks! I hope this functionality is added soon to react-native-modal, it's such an important function.
I'll test it in two days. Cannot do it right now, i'm between two airports. :p
Hey, I'll try to take a look at it soon (PRs are obviously warmly welcome) :) I still haven't tried the touchable wrapper but... doesn't it swallow the click buttons inside the modal (if any)? 🤔
Ok, i've tried and it's working, thanks!
@warka0, does it also work if you add a button inside the modal? Doesn't the TouchableWithoutFeedback
wrapper on the modal swallow any touch to components inside it?
@mmazzarolo So the modal is wrapped in TouchableWithoutFeedback
but inside the modal I have a bunch of buttons wrapped around in TouchableOpacity
and it works perfectly 👍
It was easier than I thought then :) Anyone willing to submit a PR?
Sure I'll submit an example
@jm90m thank you for the PRs! However... Guys, with @emaLorenzo 's solution the modal is closed wherever you tap, while the issue was focused on closing the modal only when clicking on the backdrop 😿
@mmazzarolo ohh, yeah looks like the main issue is more complex than it seems. I see, if I ever come across a solution, I'll submit a PR if no one has done so yet.
You guys can take a look at my repo, the idea is surrounding modal with 4 touchables. My initial solution was use a screen size touchable to perform tapToClose, a modal size touchable wrapping the modal to prevent the tap event goes up(no onPress). But then I found I can't use scrollView inside the touchable. In the end I came with up the solution I used in my repo
And also, by adjusting the flex value of the four touchables, we can change the position of the modal, which also solve #22 , I already used my modal to create a popover
Hey @zhantx , thank you for contributing to the discussion 👍
Glad there's someone else out there working with the modals.
It seems you're solution is super-flexible, at the cost of being a bit more harder to grasp to someone new to RN.
For this repo I'd like to keep the things as simple as possible, but your solution might be a good source of info for future implementations.
Also, I only took a quick look at your code, can the modal be showed without setting all the dimensions (for example: can it adapt itself to a flex content)?
P.S.: PRs ans support are super welcome here if you're interested :)
Hey @mmazzarolo , yeah I've been trying hard to simplify my modal by setting default props. so if none props are set then it will be at the center of the screen.
For you modal, to be honest adding tapToClose is impossible cuz your modal is controlled only by prop isVisible
, so the modal can't close itself directly(that's the reason why I choose ref methods open()
and close()
). What I suggest is implement four touchables in your modal, and trigger an onTapBackdrop
callback to let parent component to change the isVisible
Hey @zhantx, thanks!
so if none props are set then it will be at the center of the screen.
Does it also resize itself based on the content size? 🤔
For you modal, to be honest adding tapToClose is impossible cuz your modal is controlled only by prop isVisible, so the modal can't close itself directly
I'm not aiming at making the modal close itself directly, just to exposing an onBackdropPress
listener, which can be used to close it from the modal's parent.
(that's the reason why I choose ref methods open() and close())
I'm trying to keep react-native-modal
component as controlled as possible by staying away from refs method (at least until they're really needed).
@mmazzarolo , check this out By using four touchables, they won't interference any touch event within the modal content
@zhantx this is super-cool, thanks!
I'll take a look at a way to implement a similar solution here as soon as I have some free time.
Obviously, backward-compatible PRs are welcome too.
@mmazzarolo you are welcome mate, glad to be helpful. my modal is just a side product of my work, may not have time to contribute yours anyway, best wishes with remaining works, cheers
Dont understand the Modal is taking the full screen for the backdrop overlay. So the solution doesnt work to wrap it inside a Touchable
@zhantx very helpful thx. For my case, I only had to use 2 TouchableWithoutFeedback
(One for wrapping around the entire Modal and another for wrapping around the modal content):
as long as you don't have scrollview within modal:)
@zhantx nope its a simple menu, but good point :)
This worked for me, edit the index.js
to add a PanResponder
:
componentWillMount() {
......
// for the backdrop touch to close
this._panResponder = PanResponder.create({
// Ask to be the responder:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
},
onPanResponderMove: (evt, gestureState) => {
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
// close the modal
this._close();
},
onPanResponderTerminate: (evt, gestureState) => {
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
});
}
render() {
........
<View
{...this._panResponder.panHandlers}
onLayout={this._handleLayout}
ref={ref => (this.backdropRef = ref)}
style={[
styles.backdrop,
{backgroundColor: backdropColor, width: deviceWidth, height: deviceHeight},
]}
/>
........
}
Hey @hellomaya , thank you for sharing your snippet.
How does it handle buttons inside the modal? Doesn't the panresponder swallow the press on them?
@hellomaya's solution seems to work only if we click the backdrop shadow placed at the horizontal margins. For instance, if I click above or below the Modal, onPanResponderRelease
doesn't get triggered (I'm testing it with the official example on Android)
Hi, @mmazzarolo thanks for your asking and @jkomyno thanks for your test.
Sorry I have been busy working on something, my test initially is in my own project, and it's working well in both iOS simulator and Android device, I didn't see the issue you guys mentioned.
In the official example, it do have an issue like @jkomyno mentioned, the issue is because the default content
style had flex: 1
, so there is no room for backdrop, check index.style.js
file.
But to avoid the panresponder swallow issue, you will have to put all buttons in a separate component, like this:
<Modal ....>
<Content />
</Modal>
....
class Content extends Component {
......
return (<View><Button onPress=.....></Button></View>)
In the official example, it do have an issue like @jkomyno mentioned, the issue is because the default content style had flex: 1, so there is no room for backdrop, check index.style.js file.
That's right, but unfortunately using flex: 1
is the only way to have the modal size resize itself based on its content (obviously, I'll be glad to be proven wrong).
Yes, it's necessary. And we can try add some margin around the content
view, still be able to close the modal by tap outside. marginVertical: 25
for example, larger than 20 was recommended to avoid invalid tap at top in iOS.
Also, @mmazzarolo, thank you for this good job, I tried another modal component before, until I found that it's clearly your works are much more concise and elegant.
<Modal style={{marginVertical: 25}}>
Hi there, I've just found a solution (I do not know if someone has already found it). I add two touchable and text elements within my component and I link to them my onPress event (and its relative closing modal window method).
Here is at my screen looks like:
Below is the snippet of my code:
<Modal
isVisible={this.props.isVisible}
backdropOpacity={.9} backdropColor={'#2B333D'} animationIn={'slideInDown'} animationOut={'slideOutUp'}
style={Styles.modalContainer}
>
<TouchableWithoutFeedback
onPress={() => this.props.hideModal()}
>
<Text style={{ backgroundColor: 'blue', height: 100, width: 350 }}> </Text>
</TouchableWithoutFeedback>
<View
style={Styles.viewContainer}
>
{this.showPicker(this.props.metricFormat)}
{this.showWeekSelection()}
<TouchableOpacity
onPress={() => {
this.props.hideModal();
this.props.onSelectDay(this.state.selectedStartDate, this.state.selectedEndDate);
}}
style={Styles.buttonContainer}
>
<Text
style={Styles.text}
>
{I18n.t('select')}
</Text>
</TouchableOpacity>
</View>
<TouchableWithoutFeedback onPress={() => this.props.hideModal()} >
<Text style={{ backgroundColor: 'blue', height: 100, width: 350 }}> </Text>
</TouchableWithoutFeedback>
</Modal>
Hey @tdimnet, thank you for contributing.
The pattern you're following mimics the one showed by @zhantx (he uses 4 touchables in his solution).
I'm stil not 100% sure on adding a similar solution to react-native-modal
though, because I'm worried it might be a bit to complex to tweak later on (and might be a bit hard to configure for the users without adding a ton of props).
What do you think?
Well, this is a really though question :).
I have been through this problem during this afternoon and I think this is maybe the most graceful solution for now. It lets you hide or show the modal window as you would need.
However I think you're right: adding this to your package could make the project more complex to custom and harder to understand and it really depends on the situation you are dealing with.
On my case I was looking for several ways to close the modal window:
That being said I think we could simply update the readme file in order to other people have a better understanding and a good grasp of how to solve this common problem.
If you want, I can open a PR this week which updates the readme file and presents this solution.
Thank you for your quick reply, I would be grateful for a PR on the README :) Thanks!
Guys, my PR should fix it without too much changes.
You can also still make other handlers to e.g. buttons inside the modal.
Hi @RichardLindhout , thank you for the PR, I'll paste here what I already wrote in the PR thread:
I took a close look at it and it seems that the idea of the touchable wrapper + the custom pointerEvents still won't work if you tap on the side of the modal content, am I right?
If you press the black it will close. The modal itself will not receive touch events but the children will. See https://facebook.github.io/react-native/docs/view.html#pointerevents
So I can still have other handlers for my 'OK' button or select text inside the modal. (or date pickers for instance will still work)
Also the touchable is not a wrapper, but just below the modal. Since the modal itself is not clickable the underlying view can receive the touch events. Except when there is content in the modal.
Is there a way to implement that function? Right now when I tap on the darkened region, the modal doesn't close.