callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.72k stars 418 forks source link

Any way to disable onPress on child views while swiping? #509

Open patrik-u opened 2 years ago

patrik-u commented 2 years ago

I have an issue where onPress gets triggered on child views while I'm swiping in the pager view. Is there some way to prevent this from happening?

I tried tracking the pager state with the events:

                  <PagerView
                        onPageScrollStateChanged={onPageScrollStateChanged}
                        onMoveShouldSetResponderCapture={onMoveShouldSetResponderCapture}
                        onPageScroll={onPageScroll}

But it seems like the onPress is triggered before the state of the pager view updates.

    const onPageScrollStateChanged = (e) => {
        setPageScrolling(e.nativeEvent.pageScrollState != "idle");
        console.log("page scrolling", e.nativeEvent.pageScrollState != "idle");
    };
    const onMoveShouldSetResponderCapture = (e) => {
        return pageScrolling;
    };
    const onPageScroll = (e) => {
        console.log("scrolling");
        setPageScrolling(true);
    };

I'm using version 5.4.9 and Expo Go.

patrik-u commented 2 years ago

Found a solution from this answer: https://github.com/satya164/react-native-tab-view/issues/1241#issuecomment-1022400366

I wrapped TouchableOpacity in a new component that doesn't trigger onPress if any swipe beyond a threshold has occurred:

const TouchableOpacityFixed = ({ onPress, onPressIn, onPressOut, ...props }) => {
        const _touchActivatePositionRef = useRef(null);

        function _onPressIn(e) {
            const { pageX, pageY } = e.nativeEvent;

            _touchActivatePositionRef.current = {
                pageX,
                pageY,
            };

            onPressIn?.(e);
        }

        function _onPress(e) {
            const { pageX, pageY } = e.nativeEvent;

            const absX = Math.abs(_touchActivatePositionRef.current.pageX - pageX);
            const absY = Math.abs(_touchActivatePositionRef.current.pageY - pageY);

            const dragged = absX > 2 || absY > 2;
            if (!dragged) {
                onPress?.(e);
            }
        }

        return (
            <TouchableOpacity onPressIn={_onPressIn} onPress={_onPress} onPressOut={onPressOut} {...props}>
                {props.children}
            </TouchableOpacity>
        );
    };

And use this in my pager views, flatlists etc. to prevent onPress being triggered when swiping.

jackblackCH commented 2 years ago

Thank you! This is gold. This should/could be a flag on the PagerView object: something like preventEventsOnSwipe

csulit commented 5 months ago

Found a solution from this answer: satya164/react-native-tab-view#1241 (comment)

I wrapped TouchableOpacity in a new component that doesn't trigger onPress if any swipe beyond a threshold has occurred:

const TouchableOpacityFixed = ({ onPress, onPressIn, onPressOut, ...props }) => {
        const _touchActivatePositionRef = useRef(null);

        function _onPressIn(e) {
            const { pageX, pageY } = e.nativeEvent;

            _touchActivatePositionRef.current = {
                pageX,
                pageY,
            };

            onPressIn?.(e);
        }

        function _onPress(e) {
            const { pageX, pageY } = e.nativeEvent;

            const absX = Math.abs(_touchActivatePositionRef.current.pageX - pageX);
            const absY = Math.abs(_touchActivatePositionRef.current.pageY - pageY);

            const dragged = absX > 2 || absY > 2;
            if (!dragged) {
                onPress?.(e);
            }
        }

        return (
            <TouchableOpacity onPressIn={_onPressIn} onPress={_onPress} onPressOut={onPressOut} {...props}>
                {props.children}
            </TouchableOpacity>
        );
    };

And use this in my pager views, flatlists etc. to prevent onPress being triggered when swiping.

Using SDK 51 not working on android.