Skip to content

Conversation

@akwasniewski
Copy link
Contributor

Description

The minDistance property did not work on ios. The reason was that the GestureDetector view is sometimes getting collapsed so that it bounds are {x: 0, y:0, width:0, height:0}, then the function isWithinBounds will always return false. The solution is to check whether the view is collapsed, and if so, get the bounds of its subview instead.

Test plan

Tested on the following example, minDistance works!

Details
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {
  GestureDetector,
  GestureHandlerRootView,
  useLongPressGesture,
  usePanGesture,
  useSimultaneousGestures,
} from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  interpolateColor,
  withTiming,
} from 'react-native-reanimated';

export default function PanExample() {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const colorProgress = useSharedValue(0);
  const offsetX = useSharedValue(0);
  const offsetY = useSharedValue(0);

  const panGesture = usePanGesture({
    onBegin: () => {
      'worklet';
      colorProgress.value = withTiming(1, { duration: 150 });
    },
    onUpdate: (event) => {
      'worklet';
      translateX.value = offsetX.value + event.translationX;
      translateY.value = offsetY.value + event.translationY;
    },
    onFinalize: () => {
      'worklet';
      offsetX.value = translateX.value;
      offsetY.value = translateY.value;
    },
  });

  const longPressGesture = useLongPressGesture({
    onBegin: () => {
      'worklet';
      colorProgress.value = withTiming(1, {
        duration: 100,
      });
    },
    onActivate: () => {
      'worklet';
      colorProgress.value = withTiming(2, {
        duration: 100,
      });
    },
    onFinalize: () => {
      'worklet';
      colorProgress.value = withTiming(0, {
        duration: 100,
      });
    },
    minDuration: 1000,
    maxDistance: 10000000000000,
  });

  const gestures = useSimultaneousGestures(longPressGesture, panGesture);
  const animatedStyle = useAnimatedStyle(() => {
    const backgroundColor = interpolateColor(
      colorProgress.value,
      [0, 1, 2],
      ["navy", "purple", "blue"]
    );
    return {
      transform: [
        { translateX: translateX.value },
        { translateY: translateY.value },
      ],
      backgroundColor,
    };
  });

  return (
    <GestureHandlerRootView>
      <View style={styles.centerView}>
        <View>
          <GestureDetector gesture={gestures}>
            <Animated.View style={[styles.box, animatedStyle]} />
          </GestureDetector>
        </View>
      </View>
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  centerView: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  box: {
    height: 120,
    width: 120,
    borderRadius: 60,
    marginBottom: 100,
  },
  instructions: {
    marginTop: 8,
    textAlign: 'center',
    paddingHorizontal: 16,
  },
});

@akwasniewski
Copy link
Contributor Author

I'm not a big fan of this solution, I think it may introduce other bugs, but after a lot of tinkering around this proved to be the only working solution.

@akwasniewski akwasniewski mentioned this pull request Jan 9, 2026
@j-piasecki
Copy link
Member

Hmm, weird that the detector has the wrong dimensions while its children are sized correctly. Could you check this part to see if the values there seem correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants