React-native: [0.61][iOS 13] pageSheet/formSheet dismissal from swipe not propagated

Created on 17 Oct 2019  Â·  67Comments  Â·  Source: facebook/react-native

When the user dismisses the modal by a swipe gesture using a custom presentation style,
the event isn't caught by onDismiss.

React Native version: 0.61.0

Sample code :

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  Button,
  View,
  Modal as RNModal,
} from 'react-native';

export default class Example extends Component {
  state = {
    visible: false,
  };

  render() {
    return (
      <View style={styles.container}>
        <Button
          onPress={() => this.setState({ visible: true })}
          title="Default"
        />
        <RNModal
          visible={this.state.visible}
          onDismiss={() => console.log("on dismiss")}
          onRequestClose={() => console.log("on dismiss")}
          presentationStyle={"pageSheet"}>
          <View style={styles.content}>
            <Text style={styles.contentTitle}>Open</Text>
            <Button
              onPress={() => this.setState({ visible: false })}
              title="Close"
            />
          </View>
        </RNModal>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  },
  content: {
    backgroundColor: 'white',
    padding: 22,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 4,
    borderColor: 'rgba(0, 0, 0, 0.1)',
  },
  contentTitle: {
    fontSize: 20,
    marginBottom: 12,
  },
});

I'm no expert in iOS, but this article might give a hint.

I can fill in a PR with some help.

Bug iOS

Most helpful comment

I found a solution that works for me without the patch. Basically I have a TouchableWithoutFeedback fill the entire Modal. Then I use the onPressOut prop, which receives an event from which you can measure the locationY. What I have found is that if locationY < 0, the swipe was successful in dismissing the Modal, and in that case you can call setVisible(false). Then, next time you want to re-show the Modal, it will work. Hope it helps!

<Modal animationType="slide" presentationStyle="pageSheet" visible={ modal }>
  <TouchableWithoutFeedback onPressOut={(e) => {
    if (e.nativeEvent.locationY < 0) {
      handleModal(false)
    }}
  >
    <View style={ styles.modalOuter }>
      <View style={ styles.modalInner }>
        <Text>Hi from Modal</Text>
      </View>
    </View>
  </TouchableWithoutFeedback>
</Modal>

// ...

const styles = StyleSheet.create({
  modalInner: {
    backgroundColor: "white",
    position: "absolute",
    bottom: 0,
    width: "100%",
    height: 400
  },
  modalOuter: {
    backgroundColor: "white",
    height: "100%"
  }
});

All 67 comments

Also experiencing this.

Same here.

Having this problem as well.

same problem on iOS 13
I tried using the module "react-native-swipe-gestures", but it was a terrible experience. Now at all there is no way to swipe to set the activity flag at the modal window, it is always true.

any update on this? I was just about to use it when noticed the swipe down problem.

Also experiencing. So frustrating.

I had to change the presentationStyle to 'overFullScreen' to prevent swiping down. It's certainly no replacement for pageSheet, but if you need an onDismiss function to trigger it can be a temporary solution until this is fixed.

I am using Modal from react-native, presentationStyle ="pageSheet" which means I can slide to dismiss.

However when I do that, no function is fired.

onDismiss only fires when I close it from a button.

The modal won't open again if I do like this because it doesn't change it's state.

I've been running into this issue. I wrote a library (basically just copied over React Native's Modal code) to see if I could fix this issue. I hook into the viewDidDisappear function in the ModalHostViewController which does get called when the Native iOS gesture for dismissing the modal happens. I then manually call the onDismiss function. Here's the library: https://github.com/HarvestProfit/react-native-modal-patch

I'm not that familiar with Objective-C so I'm not sure if this is a great solution, but hopefully it helps someone. If someone more familiar thinks this is a valid solution, then I'll create a PR.

I am also experiencing this. Please release a fix for this.

Same. Does anyone knows how to prevent the swipe? Not a solution but a possible workaround for now

Same issue here. Because of that it's Modal component is useless.

Same problem with dismissing modal in presentation style. When I swipe it down to close, then state is not changed.

Here is a (not-ideal/hacky!) workaround while awaiting a proper fix if anyone is also desperate for the pull-down modal behaviour. It does not solve the problem that there's no way to fire a callback when user dismisses the modal, but enables reopening the modal after being pulled-down.

The idea behind the logic is to check if an imperative modal-open is being attempted on an "already open" modal, and forcing a couple of re-renders to reset the value on the modal.

modal

import { useState, useEffect } from 'react';

export const useModalState = initialState => {
  const [modalVisible, setModalVisible] = useState(initialState);
  const [forceModalVisible, setForceModalVisible] = useState(false);

  const setModal = modalState => {
    // tyring to open "already open" modal
    if (modalState && modalVisible) {
      setForceModalVisible(true);
    }
    setModalVisible(modalState);
  };

  useEffect(() => {
    if (forceModalVisible && modalVisible) {
      setModalVisible(false);
    }
    if (forceModalVisible && !modalVisible) {
      setForceModalVisible(false);
      setModalVisible(true);
    }
  }, [forceModalVisible, modalVisible]);

  return [modalVisible, setModal];
};

// use it the same way as before (as docs recommend)
const [modalVisible, setModalVisible] = useModalState(false)

Hope it may help some of you!

I wasn't able to get the workaround @lrholmes posted above to work. For whatever reason, the TouchableOpacity used to open the modal (and interestingly any adjacent TouchOpacity components) would no longer fire their onPress after the modal was swiped down. Desperate to get this working, I ended up putting a ScrollView around the entire modal content and used onDragEnd to flip the modal state variable. Works if your modal contents don't scroll. Not by any means ideal but was a reasonable trade-off in my situation.

@r4mdat

Just try use Modal component inside TouchableWithoutFeedback.
Then onPress will does not blocked.
And use function like @lrholmes

<TouchableWithoutFeedback>
            <Modal
                visible={enable}
                presentationStyle={'formSheet'}
            </Modal>
</TouchableWithoutFeedback>



<TouchableOpacity
            onPress={() => {
                setModalSelectPhotos(false);
                setTimeout(() => {
                    setModalSelectPhotos(true);
                }, 50);
            }}>
.......
</TouchableOpacity>

It was help me

Patch workaround for React Native 0.61.2
Here's a patch that triggers onDismiss callback when a modal is dismissed by swiping down, on iOS 13. However, there is a caveat: Once you patch the code, Xcode seems to change the default modal behavior for the entire app (at least for me), causing all modals to appear in the new style, on iOS 13. Depending on how your app is, this may be unwanted so please consider that before using the patch in production. Edit: There is no caveat anymore. Updated patch in link works as intended.

Download patch
https://gist.github.com/scarlac/ec162221e11927c52cfc9c94e7252824

Installation
You can apply it using either:

  • Manually (which is temporary - yarn may remove it) using:
    patch < react-native+0.61.2.patch in your project root folder or...
  • Automatically using patch-package (recommended)

Same issue - even with the patched versions.

@martsie You need to recompile your app. Set a breakpoints in the newly added lines to verify

Sorry, @martsie there was a line missing from my diff. You'll need to say that you're implementing a delegate as well (UIAdaptivePresentationControllerDelegate). I've updated the link. Not sure why it worked for me - perhaps I had a local modification that I forgot to include in the patch

I wasn't able to get the workaround @lrholmes posted above to work. For whatever reason, the TouchableOpacity used to open the modal (and interestingly any adjacent TouchOpacity components) would no longer fire their onPress after the modal was swiped down. Desperate to get this working, I ended up putting a ScrollView around the entire modal content and used onDragEnd to flip the modal state variable. Works if your modal contents don't scroll. Not by any means ideal but was a reasonable trade-off in my situation.

@r4mdat just put your Modal in a View with height: 0

<View style={{height:0}}><Modal>....</Modal></View>

@r4mdat

Just try use Modal component inside TouchableWithoutFeedback.
Then onPress will does not blocked.
And use function like @lrholmes

<TouchableWithoutFeedback>
            <Modal
                visible={enable}
                presentationStyle={'formSheet'}
            </Modal>
</TouchableWithoutFeedback>



<TouchableOpacity
            onPress={() => {
                setModalSelectPhotos(false);
                setTimeout(() => {
                    setModalSelectPhotos(true);
                }, 50);
            }}>
.......
</TouchableOpacity>

It was help me

This worked perfectly fine for me. I would suggest this over any other options as it takes the least amount of code and is the most understandable. Also, once the issue is fixed, it will be the simplest to revert.

Hey everyone,

Im having the same issue. onDismiss property doesn't clear the Modal component (swipe down iOS). Can someone help me?

Thanks

@aca-hakan-pinar: @scarlac solution (https://github.com/facebook/react-native/issues/26892#issuecomment-581198937) works fine for me.

Has this patch been merged? I'm on 0.61.5 and still experiencing the issue.

This still persists on 0.61.5. This bug is the entire reason why I am resorting to using a third party library for Modal.

until the problem is fixed:

import React, { useState } from 'react';
import { SafeAreaView, Modal, TouchableWithoutFeedback, Keyboard, Dimensions } from 'react-native';
import { Button } from 'react-native-elements';

const MyComponent = () => {

    const [modalView, setModalView] = useState<null|SafeAreaView>(null);
    const [isOpen, setIsOpen] = useState<boolean>(false);

    return (
        <>
            <Modal
                presentationStyle="formSheet"
                animationType="slide"
                transparent={false}
                visible={isOpen}
            >
                <TouchableWithoutFeedback
                    onPressOut={() => {
                        if(!modalView)
                            return;

                        modalView.measure((fx, fy, width, height, px, py) => {
                            if(py === Dimensions.get('window').height)
                                setIsOpen(false);
                        });
                }}>
                    <SafeAreaView ref={(ref) => setModalView(ref)}>


                    </SafeAreaView>
                </TouchableWithoutFeedback>
            </Modal>

            <Button title={'demo'} onPress={() => setIsOpen(true)} />
        </>
    );
};

and if you use focus (textInput...):

import React, { useEffect, useRef, useState } from 'react';
import { SafeAreaView, Modal, TouchableWithoutFeedback, Dimensions } from 'react-native';
import { Button } from 'react-native-elements';

const MyComponent = () => {

    const [modalView, setModalView] = useState<null|SafeAreaView>(null);
    const [isOpen, setIsOpen] = useState<boolean>(false);
    const [keyboardOpen, setKeyboardOpen] = useState<boolean>(false);

    const intervalRef = useRef<null|number>(null);



    useEffect(() => {

        if (keyboardOpen)
            {intervalRef.current = setInterval(() => {
                tryClose();
            }, 500);}

        else if (intervalRef.current != null) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
        }

        return () => {
            if (intervalRef.current)
                {clearInterval(intervalRef.current);}
        };

    },[keyboardOpen]);


    const tryClose = () => {
        if (!modalView)
            {return;}

        modalView.measure((fx, fy, width, height, px, py) => {
            if (py === Dimensions.get('window').height){
                setIsOpen(false);
                setKeyboardOpen(false);
            }
        });
    };

    return (
        <>
            <Modal
                presentationStyle="formSheet"
                animationType="slide"
                transparent={false}
                visible={isOpen}
            >
                <TouchableWithoutFeedback
                    onBlur={() => setKeyboardOpen(false)}
                    onFocus={() => setKeyboardOpen(true)}
                    onPressOut={tryClose}>
                    <SafeAreaView ref={(ref) => setModalView(ref)}>


                    </SafeAreaView>
                </TouchableWithoutFeedback>
            </Modal>

            <Button title={'demo'} onPress={() => setIsOpen(true)} />
        </>
    );
};

or:

import React, { useEffect, useRef, useState } from 'react';
import { SafeAreaView, Modal, Dimensions } from 'react-native';
import { Button } from 'react-native-elements';

const MyComponent = () => {

    const [modalView, setModalView] = useState<null|SafeAreaView>(null);
    const [isOpen, setIsOpen] = useState<boolean>(false);
    const intervalRef = useRef<null|number>(null);

    useEffect(() => {

        const clearLoop = () => {
            if(intervalRef.current)
                clearInterval(intervalRef.current);
            intervalRef.current = null;
        };

        if(isOpen && modalView)
            intervalRef.current = setInterval(() => tryClose(), 500);
        else if (intervalRef.current != null)
            clearLoop();

        return () => clearLoop();

    },[isOpen, modalView]);


    const tryClose = () => {
        if(!modalView)
            return;

        modalView.measure((fx, fy, width, height, px, py) => {
            if (py === Dimensions.get('window').height)
                setIsOpen(false);
        });
    };

    return (
        <>
            <Modal
                presentationStyle="formSheet"
                animationType="slide"
                transparent={false}
                visible={isOpen}>
                <SafeAreaView ref={(ref) => setModalView(ref)}>



                </SafeAreaView>
            </Modal>

            <Button title={'demo'} onPress={() => setIsOpen(true)} />
        </>
    );
};

@CodingByJerez

Thanks a lot for posting this but when it comes to the use of SafeAreaView - that sure isn't working for me on iOS 13.2.3. It has to be a regular View. (Possibly related to this?)

Plus - if the user swipes down immediately before tapping on the modal, it seems that onPressOut is not triggered. Which apparently can be resolved by using TouchableOpacity.

And so overall, I ended up with this working for me (since it's possible to have the ref on TouchableOpacity, I figured why not. One just has to watch out that it spans the whole modal, like with flex: 1. And the View apparently becomes unnecessary in this case.):

  const [modalView, setModalView] = useState<null | TouchableOpacity>(null);

  return (
    <Modal
      animationType="slide"
      presentationStyle="pageSheet"
      visible={isVisible}
      onRequestClose={onClose}
    >
      <TouchableOpacity
        activeOpacity={1}
        onPressOut={() => {
          if (!modalView) return;

          modalView.measure((fx, fy, width, height, px, py) => {
            if (py === Dimensions.get('window').height) onClose();
          });
        }}
        ref={(ref) => setModalView(ref)}
        style={{
          flex: 1,
        }}
      >
        <Text>Hello World!</Text>
        <Button onPress={onClose} title="Hide Modal" />
      </TouchableOpacity>
    </Modal>
  );

@s-h-a-d-o-w
I run it on iOS 13.3, and I don't have a problem.
Have you tried the loop? (This is the safest way)
(Check 'PY' It may vary depending on your use in 0 and Dimensions.get('window').height)

import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
import { SafeAreaView, Modal } from 'react-native';


interface IProps {
    visible:boolean,
    onVisible(bool:boolean):void,
}

const MyComponent:FunctionComponent<IProps> = ({visible, onVisible, children}) => {

    const [modalView, setModalView] = useState<null|SafeAreaView>(null);
    const [isOpen, setIsOpen] = useState<boolean>(false);
    const intervalRef = useRef<null|number>(null);

    useEffect(() => setIsOpen(visible),[visible]);


    useEffect(() => {
        if(visible !== isOpen)
            onVisible(isOpen);

        const clearLoop = () => {
            if(intervalRef.current){
                clearInterval(intervalRef.current);
                intervalRef.current = null;
            }
        };

        if(isOpen && modalView)
            intervalRef.current = setInterval(() => tryClose(), 500);
        else if (intervalRef.current != null)
            clearLoop();

        return () => clearLoop();

    },[isOpen, modalView]);


    const tryClose = () => {
        if(!modalView)
            return;

        modalView.measure((fx, fy, width, height, px, py) => {
            if (py === 0)
                setIsOpen(false);
        });
    };

    return (
        <Modal
            presentationStyle={'formSheet'}
            animationType={'slide'}
            transparent={false}
            visible={isOpen}
            onRequestClose={() => setIsOpen(false)}>
            <SafeAreaView ref={(ref) => setModalView(ref)}>
                {children}
            </SafeAreaView>
        </Modal>
    );

};

I'm on react native 0.62.0 and its still not working for me

Yes, this still continues to be a bug. I am thinking that RN core is planning to get rid of Modal from core altogether and move it to react-native-community and thus there has been no interest in fixing this?? still not sure...

@scarlac Patch workaround doesn't work anymore in RN 0.62.0… RCTModalManager.h file not found in RCTModalHostView.m.

@jdanthinne you could try this gist https://gist.github.com/r0b0t3d/3c9f77434e6fbcfa78698dcf57614fad

<Modal
      visible={visible}
      animationType="slide"
      presentationStyle="pageSheet"
      onRequestClose={onClose}
    >
</Modal>

@r0b0t3d Working fine. Thanks!

Anyone knows why this issue is still marked as closed despite the issue still happening? @r0b0t3d do you know if there's a pull request for the gist you posted?

@noambonnie i think that's because they are planning to remove Modal out of core and move it to react-native-community; there's already a lib out there react-native-community/react-native-modal.

I am not a 100% sure if this is true though.

@annjawn I see no indication that they'll move it out. That library relies on the modal component, does not provide the same default look, and has no native integrations like page sheets.
Is there a discussion where they are considering it?

@scarlac as I said, I am not 100% certain that's the plan but seeing how many of the things are with core, this could very well be a possibility. Yes, the community modal doesn't closely support the navigations and native integrations but I would venture a guess that it won't be a huge deal to support native view controller integrations there for both iOS and Android. But again , all of this is a big speculation on my side.

@jdanthinne Does @r0b0t3d ’s solution working ? Would you be able to close the modal programmatically ?

@amilaDulanjana Yes, working fine for me.

@annjawn that package re-uses the existing Modal from react-native, and adds styling on top. It's purely JS and has no native code.

@jdanthinne Does @r0b0t3d ’s solution working ? Would you be able to close the modal programmatically ?

not working for me..any other ideas so far?

I have it working with combination of two solutions mentioned in this thread..

  1. Wrap <Modal /> with <View style={{ height: 0}} />
    This allows the button triggering the modal to be clicked again when the PageSheet modal is swiped down

  2. Ensure that you always set isVisible to false first, then set to true to "refresh" the modal state.

I guess this will leave the modal instance hanging around when swiped down, but setting setModalOpen to false on the unmount of component containing the <PageSheetModal /> should help?

const [isModalOpen, setModalOpen] = React.useState<boolean>(false);

const showModal = (): void => {
    setModalOpen(false);
    requestAnimationFrame(() => setModalOpen(true));
};

// React.useEffect() to setModalOpen to false on component unmount

Modal example

import * as React from 'react';
import { Modal, View } from 'react-native';
import { IPageSheetModalProps } from './PageSheetModal.types';

export function PageSheetModal(
    props: React.PropsWithChildren<IPageSheetModalProps>
): React.ReactElement {
    const { isVisible, children } = props;

    return (
        <View style={{ height: 0 }}>
            <Modal presentationStyle="pageSheet" visible={isVisible} animationType="slide">
                {children}
            </Modal>
        </View>
    );
}

I've also tried using <TouchableWithoutFeedback />, but it caused issues if I had <ScrollView /> inside. The above example also works with <ScrollView /> inside the modal.

Patch workaround for React Native 0.61.2
Here's a patch that triggers onDismiss callback when a modal is dismissed by swiping down, on iOS 13. ~However, there is a caveat: Once you patch the code, Xcode seems to change the default modal behavior for the entire app (at least for me), causing all modals to appear in the new style, on iOS 13. Depending on how your app is, this may be unwanted so please consider that before using the patch in production.~ Edit: There is no caveat anymore. Updated patch in link works as intended.

Download patch
https://gist.github.com/scarlac/ec162221e11927c52cfc9c94e7252824

Installation
You can apply it using either:

* Manually (which is temporary - yarn may remove it) using:
  `patch < react-native+0.61.2.patch` in your project root folder or...

* Automatically using [patch-package](https://www.npmjs.com/package/patch-package) (recommended)

This was the solution for me
I'm using react-native 0.61.5

before opening modal, set visible state to false, then set visible state to true again, because if modal already open, closed and open again

showModal = () => {
this.setState({ modalVisible:false},function(){
this.setState({ modalVisible: true })
})
}

I've updated to RN 0.62.2 and the bug persist.

still not working in RN 0.62.2 onDismiss is never called

I used react-native-modal as a small workaround for this issue https://github.com/react-native-community/react-native-modal. It works as expected.

Any solution for Expo users?

Will somebody create a PR with @scarlac’s patch?

A patch has been published and then revert on 0.62.2 as we can see here https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#v0622 maybe a new patch will come with v0.63.0-rc.2 ?

Ah yes, that change. Thanks for reminding me. There is no pending fix that I know of, if it existed it would most likely have been linked here.

Now that I think about it, I had asked @neilkimmett to share a fix he has–which iirc I observed from one of the threads discussing this issue. Perhaps he has time now, or at least to share it in such a way that one of you can make it into a proper PR.

Not working 0.62.2.

I found a solution that works for me without the patch. Basically I have a TouchableWithoutFeedback fill the entire Modal. Then I use the onPressOut prop, which receives an event from which you can measure the locationY. What I have found is that if locationY < 0, the swipe was successful in dismissing the Modal, and in that case you can call setVisible(false). Then, next time you want to re-show the Modal, it will work. Hope it helps!

<Modal animationType="slide" presentationStyle="pageSheet" visible={ modal }>
  <TouchableWithoutFeedback onPressOut={(e) => {
    if (e.nativeEvent.locationY < 0) {
      handleModal(false)
    }}
  >
    <View style={ styles.modalOuter }>
      <View style={ styles.modalInner }>
        <Text>Hi from Modal</Text>
      </View>
    </View>
  </TouchableWithoutFeedback>
</Modal>

// ...

const styles = StyleSheet.create({
  modalInner: {
    backgroundColor: "white",
    position: "absolute",
    bottom: 0,
    width: "100%",
    height: 400
  },
  modalOuter: {
    backgroundColor: "white",
    height: "100%"
  }
});

I found much simpler workaround, it looks like instead of:

onPress={() => setModalVisible(true)

You might try this

onPressIn={() => setModalVisible(false)}
onPressOut={() => setModalVisible(true)}

Of course, that means that your button component should provide you these events (TouchableOpacity does)

I found much simpler workaround, it looks like instead of:

onPress={() => setModalVisible(true)

You might try this

onPressIn={() => setModalVisible(false)}
onPressOut={() => setModalVisible(true)}

Of course, that means that your button component should provide you these events (TouchableOpacity does)

that's the way to go! Thanks

I found much simpler workaround, it looks like instead of:

onPress={() => setModalVisible(true)

You might try this

onPressIn={() => setModalVisible(false)}
onPressOut={() => setModalVisible(true)}

Of course, that means that your button component should provide you these events (TouchableOpacity does)

Worked like magic !
If you are getting accidental taps (while scrolling in a list e.t.c), try doing:

onPressIn={() => setModalVisible(false)} // 👉  as before
onPress={() => setModalVisible(true)} // 👉  onPress can tell when you are scrolling and not tapping

I have merged solutions of @thomasttvo and @lrholmes and it has worked now. 💯

I found much simpler workaround, it looks like instead of:

onPress={() => setModalVisible(true)

You might try this

onPressIn={() => setModalVisible(false)}
onPressOut={() => setModalVisible(true)}

Of course, that means that your button component should provide you these events (TouchableOpacity does)

This works 100%! Even logically, it makes sense.

PS. @gboyegadada thanks for the added tip. I encountered that as well

I found much simpler workaround, it looks like instead of:

onPress={() => setModalVisible(true)

You might try this

onPressIn={() => setModalVisible(false)}
onPressOut={() => setModalVisible(true)}

Of course, that means that your button component should provide you these events (TouchableOpacity does)

Thanks for the fix! Closing this out.

For me a pretty quick fix was more in how the modal is triggered. I have a button that used to just set the modal's viewable variable to true when the button was pressed; however, now when the button is pressed I set the viewable to false and then setTimeout for 10 ms and then set the viewable variable to true. The change is pretty seamless, the user won't notice the 10ms. This is better for me because I have a scrollView in the modal and wrapping the modal in a TouchableOpacity was not working well with scrolling.

onPressFilter() {
    this.setState({
      filterModalViewable: false,
    })
    setTimeout(() => this.setState({
      filterModalViewable: true,
    }), 10)
}

Issue I'm still having is sending any data back to the parent component when the swipe occurs, but at least now the modal can be opened again and the scrolling works perfectly.

Following @lieberscott: BUG FIX WITH BUTTON CLOSING MODAL

Going along with what @lieberscott said above, you have to go one step further for iOS 13 if you have a button that closes the modal. I've found that while @lieberscott's example worked perfectly, there's still a SERIOUS bug that can be very frustrating to fix. Be very careful when using the component "Button" from "react-native".

If you swipe down from anywhere in the Modal, @lieberscott's function will work perfectly. HOWEVER, if you click on a Button component and swipe down at the same time to dismiss the Modal, no event is fired. After hours of tinkering, I've found that you can bypass this by using TouchableOpacity instead of a Button. I have no idea why that is but upon using TouchableOpacity, @lieberscott's function worked perfectly again. Hope this helps anyone who has been running into this issue!

Hey,
I have built a patch file for react-native 0.63.3 that fixes this issue: https://gist.github.com/ps73/012b8b97bb7866db4b2b4636a8396d98

If you embed this to your project you just have to set the onRequestClose property and the swipe down works as expected for formSheet and pageSheet modals on iOS. If you don't set onRequestClose the swipe down is disabled.

@ps73 It’s a little hard for me to judge if this is generic enough that it would be a solution for all. If you do think or want to let others weigh in, mind creating a PR for it?

Eu encontrei uma solução alternativa muito mais simples, parece em vez de:

onPress = { ( )  =>  setModalVisible ( true )

Você pode tentar isso

onPressIn = { ( )  =>  setModalVisible ( false ) } 
onPressOut = { ( )  =>  setModalVisible ( true ) }

Claro, isso significa que o componente do botão deve fornecer esses eventos (TouchableOpacity fornece)

Show man!
very good.

@vasiliicuhar solution worked for me with RN 0.62.2:

      <Modal
        visible={visible}
        animationType={animation || 'slide'}
        onRequestClose={onClose}
        presentationStyle={presentationStyle}
        onDismiss={onDismiss}
        statusBarTranslucent={statusBarTranslucent}
      >
        <TouchableWithoutFeedback
          // for bug which prevents dismiss from firing on swipe close
          // https://github.com/facebook/react-native/issues/26892
          onPressOut={onDismiss}
        >
          <SafeAreaView
            testID={generateDismissibleModalTestId(testIDBase)}
            style={styles.SafeAreaView}
            forceInset={{ bottom: 'always' }}
          >
            {this.content()}
          </SafeAreaView>
        </TouchableWithoutFeedback>
      </Modal>
Was this page helpful?
0 / 5 - 0 ratings