This is what I'm trying to do, basically, click Open Modal button to open modal, then click Close Modal button inside modal to close it. The two pictures below are how both cases should look like.
This is my code:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
componentWillUpdate() {
if(PlatForm.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
LayoutAnimation.easeInEaseOut();
}
render() {
return (
<View style={{ ... }}>
<TouchableOpacity onPress={() => this.setState({ showModal: true })}>
<Text style={{ ... }}>
Open Modal
</Text>
</TouchableOpacity>
<Modal visible={this.state.showModal} animationType='slide'>
<View style={{ ... }}>
<TouchableOpacity onPress={() => this.setState({ showModal: false })}>
<Text style={{ color: 'white', fontSize: 20 }}>
Close Modal
</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
}
}
Everything works as expected when running on an IOS simulator, but problems rises when running on an iPhone. When press Close Modal, modal disappears for like half a second, then reopens itself again, and this time, Close Modal button won't work, I cannot re-close modal. All I can do is to re-build project. However, when I delete componentWillUpdate(), it works again, both on simulator and on iPhone
I believe this is a bug introduced in a recent version of React Native. Your best bet, for now, is to disable LayoutAnimation. Not ideal...I know.
See the discussion on Github here: https://github.com/facebook/react-native/issues/16182
Related
"react-native-loading-spinner-overlay" is not working when modal is open in iOS. If I set modal visible to false, it works.
(Android is working well in any case.)
That is my code.
When modal is open, if I click upload button on modal,
_onUpload = () => {
this.setState({ isLoading: true }) //-----> Loading spinner is not working.
}
If I make like this
_onUpload = () => {
this.setState({ modalVisible:false }) //-----> After modal turns off
setTimeout(() => {
this.setState({ isLoading: true }) //----> Loading spinner works.
}, 500);
}
render
render() {
return (
<View>
<Spinner
visible={this.state.isLoading}
textContent={'Loading...'}
textStyle={{ color: 'white' }}
/>
</View>
)
}
I used modal from 'react-native-modal'
import Modal from 'react-native-modal';
<Modal
isVisible={modalVisible}
backdropColor="#B4B3DB"
backdropOpacity={0.8}
animationIn="zoomInDown"
animationOut="zoomOutUp"
animationInTiming={1000}
animationOutTiming={1000}
backdropTransitionInTiming={1000}
backdropTransitionOutTiming={1000}>
<View style={styles.modalBody}>
<TouchableOpacity onPress={() => this._onUpload()} >
<Text>Upload</Text>
</TouchableOpacity>
</View>
</Modal>
Why it is not working on only iOS?
How to make it work on iOS when modal is open?
You cannot use one Modal on another Modal in ios,
you have to close the first Modal before opening a second Modal.
like,
setstate({isLoading:false,modalVisible:true})
or you can use ternary like,
{modalVisible?
<View style={styles.modalBody}>
<TouchableOpacity onPress={() => this._onUpload()} >
<Text>Upload</Text>
</TouchableOpacity>
</View: null}
Add a timeout to wait for the first Modal to close.
setTimeout(() => {
setstate({modalVisible:true})
}, 2000);
Creating your own spinner is the best option I come across for this issue.
{showSpinner && (
<View style={{ ...StyleSheet.absoluteFill, zIndex: 1 }}>
<ActivityIndicator color={Colors.gray900} size="large" style={{ flex: 1, backgroundColor: Colors.gray300, opacity: 0.3 }} />
</View>
)}
I have a Modal wrapping a KeyboardAvoidingView wrapping a few animated components. One of them is a TextInput and the other is a Button. When clicking on the button, keyboard is first hidden, and then need to click once again to reach the buttons "onPress"
Looked into the solution of wrapping the components with a scroll view and using a the prop keyboardShouldPersistTaps={'always'} but that doesn't work.
constructor(props) {
super(props);
this.paddingInput = new Animated.Value(0);
this.state = {
modalVisible: false,
otherTip: '',
}
}
renderHoveringNote = () => {
const {total, currentTipPercent} = this.props.rootStore.orderStore;
return (
<View>
<KeyboardAvoidingView style={{flex: 1}}>
<Animated.View style={{
marginBottom: this.paddingInput,
flex: 1
}}>
<View>
<Text>Enter custom amount</Text>
</View>
<TextInput
onChangeText={value => {
this.setState({otherTip: value})
}}
autoFocus
value={this.state.otherTip}
/>
<Button title='Save'
onPress={()=>{
...do some stuff here
this.setState({modalVisible: false});
Keyboard.dismiss();
}}
</Animated.View>
</KeyboardAvoidingView>
</View>
)
};
renderOtherTipModal = () => {
return (
<Modal
isVisible={this.state.modalVisible}
onBackdropPress{()=>this.setState({modalVisible:false})}
style={{margin: 0, flex: 1}}
hideModalContentWhileAnimating={true}
>
{this.renderHoveringNote()}
</Modal>
)
};
One click should reach the onPress of the button
Figured it out - I had a few Scroll Views as parents to the Modal. It is important to have ALL of the parent Scroll Views to have keyboardShouldPersistTaps={'always'}.
After adding that, it worked great.
I have modal for filtering search resluts , something like foursquare app . I have filters in diffrent categories and I need to use tabs for each category . for example when user clicks each tabs it shows the filters related to that tab . and user can select checkboxes or radio buttons . and at the end when user checks all of their needed filters I need to make http request with the new filters.
Something like the image below . I created the modal but I need the functionality for tabs and at the end making the api request with the selected options:
You can also create custom tabs using <Text> with state and depending on a state value render a View associated with that tab. for example
state = {
modalVisible: false,
currentTab: 1,
};
onTabClick = (currentTab) => {
this.setState({
currentTab: currentTab,
});
};
// inside render
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={styles.tabs}>
<Text
onPress={() => {
this.onTabClick(1);
}}
style={[
styles.tabTextStyle,
this.state.currentTab === 1 ? styles.tabUnderline : null,
]}>
GENDER
</Text>
...
</View>
{this.state.currentTab === 1 && (
<View>
<Text>GENDER</Text>
</View>
)}
...
snack example
Modal is just a Container like View. You can draw anything inside it.
First, import {Modal} from 'react-native'
Then, in your modal, embed anything what you want:
<Modal visible={ this.state.modal }
animationType="fade" transparent={true}
onRequestClose={_ => this.setState({ modal: false }) }>
<View>
{/*
Do anything. Its an open ground.
Whatever component, styles, props and/or anything else you want, you can design
*/}
{/* For example, I am adding a close button */}
<TouchableOpacity style={{ alignSelf: 'flex-end' }} onPress={_ => this.setState({ modal: false }) }>
<Icon type="FontAwesome" name='times' style={ styles.closeIcon } />
</TouchableOpacity>
</View>
</Modal>
And you can open your modal from anywhere like:
<TouchableOpacity style={ styles.button } onPress={_ => this.setState({ modal: true }) }>
<Text style={ styles.buttonText }>Open Modal</Text>
</TouchableOpacity>
Finally, for tabs, you can use either of:
NativeBase Tab Component
React Native Tab View
I use React-native and Expo for my mobile client.
Flow:
User taps to open up a modal from root view
Modal has Expo's Share and WebView invoking buttons.
When user taps to open either of them, nothing happens.
When the modal is closed manually by the user, then the WebBrowser/Share are suddenly invoked, and opened on the root view that the modal is a child of.
Both functions to call the Share and WebBrowser invokers are in the modal's controller.
Partial Content of the modal controller:
openBrowser = (url) => {
WebBrowser.openBrowserAsync(url);
}
shareModel = (url) => {
Share.share({
url: url
}) .then(console.log(`Shared ${url}`))
.catch(err => console.log(err))
}
render() {
return (
<Modal
visible={this.props.visible}
animationType={'slide'}
onRequestClose={this.props.onRequestClose}
>
<View style={styles.modalContainer}>
<View style={styles.oneButton}>
<TouchableOpacity style={styles.toolbarButton} onPress={() => this.openBrowser(this.props.model.public_url)}>
<SimpleLineIcons
name='globe'
size={29}
style={{ marginTop: 1 }}
color='#7366E3'
/>
<Text style={{ color: '#7366E3', fontSize: 13, marginTop: 1 }}>View</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)
}
Any ideas? This exact code worked well in the past and something changed.
It could be due to the new Expo client's SDK26. One of their breaking changes include methods in the WebBrowser, see https://blog.expo.io/expo-sdk-v26-0-0-is-now-available-2be6d9805b31
Disclaimers:
Only can test on iPhone emulator atm.
React-Native 0.49
Mac OSX High Sierra
I want to create a modal which gets its props from a parent component.
As below:
const Modal = ({ showModal, closeModal }) => (
<Modal
animationType="slide"
transparent={false}
visible={showModal}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={{marginTop: 22}}>
<Text>Hello World!</Text>
<TouchableHighlight onPress={() => closeModal() }>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</Modal>
);
This is the parent example:
<View>
<Modal
showModal={this.state.showModal}
closeModal={() => this.setState({ showModal: false })}
/>
<ScrollView>
{elements.map(element => {
return (
<Card key={element.id}>
<Badge onPress={() => this.setState({ showModal: true })>
<Text>Show</Text>
</Badge>
</Card>
);
})}
</ScrollView>
</View>
When I click the show modal button the modal pops-up as expected but when I click closeModal then the modal disappears and reappears again but this time I cannot interact with it, the UI seems as if it is frozen, I have to then restart the emulator.
If I copy and paste the code straight from the React-Native docs:
https://facebook.github.io/react-native/docs/modal.html
The modal works fine. It is a self-contained component though.
Any help/advice would be much appreciated.
Regards,
Emir
After painfully rebuilding the component from scratch I see there was a unsuspected culprit:
componentWillUpdate() {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
LayoutAnimation.easeInEaseOut();
}
When I removed this code the modal worked fine but when I added it back it froze when I tried to close. This seems to be some animation conflict in iOS cant confirm for Android.
When I added a timeout of 1000ms the screen revealed a little more before it froze again.
So for now if someone has the same issue look for multiple animations being called.
Hope this helps someone, and if you have a better way of solving it please do let me know.
Regards,
Emir
This is a know issue and nothing to do with your code.
See here: https://github.com/facebook/react-native/issues/16895
Make sure that your <Modal/> is wrapped in a <View/>.
Very late, but issue still exist in latest version, And only solution i found is make different views in render method.
one for modal and one for other component.
render() {
if (showErrorModal) {
return (
<ModalError message={message} visible={showErrorModal} handleBack={this.handleBack} />
);
}
return (
<ScrollView style={{ flex: 1 }}>
<View style={{ padding: 10, paddingVertical: 20 }}>
{!active ? this.fieldlabel() : this.fieldSelect()}
</View>
// remove this one, do not use here. it will block the UI
{* <ModalError message={message} visible={showErrorModal} handleBack={this.handleBack} /> *}
</ScrollView>
);
}
}
Inside your Parent component, create a function will set showModal to false.
Parent
closeModal = () => {
this.setState({
showModal: false
});
}
Then you need to pass it down to your Modal, via props.
<Modal showModal={this.state.showModal} closeModal={this.closeModal} />
Inside of your Modal, change:
<TouchableHighlight onPress={() => this.closeModal() }>
To:
<TouchableHighlight onPress={closeModal}>
Why do u use this.closeModal()? Use the one u are getting from the props,i.e just closeModal().
<TouchableHighlight onPress={() => closeModal() }>