react-native-deck-swiper icon indicating copy to clipboard operation
react-native-deck-swiper copied to clipboard

[iOS on expo 52] - Swipe-Back Animates Last Card, Then Jumps Another Card Back Without Animation, Breaking Swiper! (Might be the latest version of React Native)

Open ShmuelPickRanky opened this issue 1 year ago • 5 comments

On iOS with Expo SDK 52, using the swipe-back gesture results in the following critical issue:

  1. The last card animates back correctly.
  2. Immediately after, the swiper skips an additional card backward without animation.
  3. The swiper becomes unresponsive to further interactions.

NOTE: This happens regardless of the infinite prop value. If the swiper is on the first card, swiping back unexpectedly takes it to the last card instead of stopping at the beginning. This severely impacts usability on iOS.

Steps to Reproduce:

  1. Swipe a card back using the swipe-back gesture.
  2. Observe that the card animates back correctly.
  3. Notice that it skips another card backward without animation and the swiper becomes unresponsive.

Expected Behavior: The swiper should stop at the previous card with no unexpected jumps and remain responsive to further actions.

Environment: Expo SDK: 52 Platform: iOS Library Version: react-native-deck-swiper v2.0.17

This is a critical bug for swipe-back functionality. Any help or fix would be greatly appreciated! If needed, I can provide additional context or examples to help debug this issue.

Code example:

import {
    useSafeAreaInsets,
} from 'react-native-safe-area-context';
import { Text } from 'react-native-paper';
import { useState, useEffect, useRef } from 'react';
import { View, StyleSheet, Dimensions, Image, Platform } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import Swiper from 'react-native-deck-swiper';

const { width, height } = Dimensions.get('window');

const CardsSwiper = () => {
    const swiperRef = useRef(null);
    const [cards] = useState([
        { id: '1', text: 'Card 1' },
        { id: '2', text: 'Card 2' },
        { id: '3', text: 'Card 3' },
        { id: '4', text: 'Card 4' },
        { id: '5', text: 'Card 5' },
        { id: '6', text: 'Card 6' },
    ]);
    const [measurements, setMeasurements] = useState(null);
    const { top, bottom } = useSafeAreaInsets();
    const headerHeight = Platform.select({ android: top + 56, ios: top + 44 });
    const tabHeight = bottom + 49;

    useEffect(() => {
        setMeasurements({
            width: width,
            height: height - headerHeight - tabHeight,
        });
    }, [top]);

    if (!measurements) {
        return null;
    }

    return (
        <View style={{ width: measurements.width, height: measurements.height }}>
            <Swiper
                ref={swiperRef}
                cards={cards}
                keyExtractor={(card) => card.id}
                renderCard={(card) => (
                    <View style={styles.cardContainer}>
                        <View
                            style={{
                                width: '100%',
                                height: '86%',
                            }}
                        >
                            <Image
                                source={{
                                    uri: 'https://media.gq.com/photos/623df9b425ea3c548d988e1d/master/w_1600%2Cc_limit/GQ0422_Gunna_07.jpg',
                                }}
                                style={{ width: '100%', height: '100%' }}
                                resizeMode="cover"
                            />
                            <Text style={{ color: 'red', position: 'absolute', top: 0 }}>
                                {card.text}
                            </Text>
                        </View>
                        <LinearGradient
                            pointerEvents={'none'}
                            colors={[
                                'transparent',
                                'rgba(0, 0, 0, 0.1)',
                                'rgba(0, 0, 0, 0.2)',
                                'rgba(0, 0, 0, 0.3)',
                                'rgba(0, 0, 0, 0.4)',
                                'rgba(0, 0, 0, 0.5)',
                                'rgba(0, 0, 0, 0.6)',
                                'rgba(0, 0, 0, 0.7)',
                                'rgba(0, 0, 0, 0.8)',
                                'rgba(0, 0, 0, 0.9)',
                                'rgba(0, 0, 0, 0.99)',
                                'black',
                                'black',
                            ]}
                            style={[
                                styles.linearGradient,
                                { height: measurements.height * 0.8 },
                            ]}
                        />
                    </View>
                )}
                onTapCard={() => {
                    swiperRef.current?.swipeBack();
                }}
                swipeBackCard={true}
                stackSize={3}
                stackScale={3}
                stackSeparation={0}
                cardIndex={0}
                showSecondCard={true}
                verticalSwipe={true}
                horizontalSwipe={true}
                cardVerticalMargin={0}
                cardHorizontalMargin={0}
                containerStyle={{
                    width: measurements.width,
                    height: measurements.height,
                }}
                cardStyle={{
                    width: measurements.width,
                    height: measurements.height,
                }}
                backgroundColor={'transparent'}
            />
        </View>
    );
};

const styles = StyleSheet.create({
    cardContainer: {
        flex: 1,
        borderRadius: 10,
        overflow: 'hidden',
        backgroundColor: 'white'
    },
    linearGradient: {
        position: 'absolute',
        bottom: 0,
        width: '100%',
    },
});

ShmuelPickRanky avatar Dec 22 '24 22:12 ShmuelPickRanky

Any update on this?

ShmuelPickRanky avatar Jan 02 '25 12:01 ShmuelPickRanky

opening an issue won’t really do anything unless a PR with a fix is opened as well. I’d be happy to merge a fix for this

webraptor avatar Jan 02 '25 14:01 webraptor

opening an issue won’t really do anything unless a PR with a fix is opened as well. I’d be happy to merge a fix for this

Thanks for the response! I’m happy to collaborate and try to fix this with someone, but unfortunately, I wasn’t able to resolve it on my own. If anyone has suggestions or is willing to work together on a solution, I’d really appreciate it!

ShmuelPickRanky avatar Jan 02 '25 16:01 ShmuelPickRanky

Can someone help me with this issue? It’s critical - I’m having to build my own swiper library because of it..

ShmuelPickRanky avatar May 13 '25 16:05 ShmuelPickRanky

@ShmuelPickRanky Hello I'm facing the same issue, did you find any solution ?

elalami-m avatar Sep 17 '25 16:09 elalami-m