question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Pan gesture inside a ScrollView blocks scrolling

See original GitHub issue

Description

I have a use case where I have a draggable element which I implement using Pan Gesture. This element is being rendered inside a ScrollView and block scrolling when attempting to scroll in the pan area (of the element)

Assuming that the pan conflicts with the scroll I tried to approach it differently and added a LongPress gesture that once started enables the panning, so as long as the user didn’t long press the element, the pan gesture should not block the scrolling.

I pretty much implemented this example with minor changes https://docs.swmansion.com/react-native-gesture-handler/docs/gesture-composition#race

This is the LongPress gesture

  const longPressGesture = Gesture.LongPress()
    .onStart(() => {
      isDragging.value = true;
    });

This is the Pan gesture

  const dragGesture = Gesture.Pan()
    .onStart(() => {...})
    .onUpdate(event => {....})
    .onFinalize(() => {
         isDragging.value = false;
    })
    .simultaneousWithExternalGesture(longPressGesture);

And finally

const composedGesture = Gesture.Race(dragGesture, longPressGesture);

<GestureDetector gesture={composedGesture}>
  <View reanimated>{props.children}</View>
</GestureDetector>

I was thinking on invoking dragGesture.enabled(false/true) to enable/disable the panning, but TBH, I’m not sure where to call it. Any ideas how to approach this?

Platforms

  • iOS
  • Android
  • Web

Screenshots

Steps To Reproduce

  1. Either use the code mention in the description in order to reproduce the full problem, or use the one from this guide and wrap the component with a ScrollView
  2. The code snippet below is more focused on the issue with a ScrollView that wraps a pan gesture component and how it blocks the scrolling

Expected behavior

Actual behavior

Snack or minimal code example

This is a small code snippet that demonstrate the general issue of pan/scroll not working together. Scrolling on the DragComponent area will not work, only below it will work

const DragComponent = props => {
  const gesture = Gesture.Pan();

  return (
    <GestureDetector gesture={gesture}>
      <View reanimated>{props.children}</View>
    </GestureDetector>
  );
};

const Main = () => {
    return (
      <ScrollView>
        <DragComponent>
          <View style={{width: '100%', height: 400, backgroundColor: 'red'}}/>
        </DragComponent>
      </ScrollView>
    );
}

Package versions

  • React: 17.0.2
  • React Native: 0.66.4
  • React Native Gesture Handler: 2.3.0
  • React Native Reanimated: 2.4.1

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

5reactions
j-piaseckicommented, Mar 17, 2022

Hi! Gestures blocking scrolling when they are placed inside ScrollView is by design, otherwise you wouldn’t be able to use them. The idea of requiring LongPress before Pan can activate is a good way to get around that, but unfortunately, you cannon accomplish this using Race and Simultaneous modifiers. You can accomplish this using touch events:

const TOUCH_SLOP = 5;
const TIME_TO_ACTIVATE_PAN = 400;

const DragComponent = (props) => {
  const touchStart = useSharedValue({ x: 0, y: 0, time: 0 });

  const gesture = Gesture.Pan()
    .manualActivation(true)
    .onTouchesDown((e) => {
      touchStart.value = {
        x: e.changedTouches[0].x,
        y: e.changedTouches[0].y,
        time: Date.now(),
      };
    })
    .onTouchesMove((e, state) => {
      if (Date.now() - touchStart.value.time > TIME_TO_ACTIVATE_PAN) {
        state.activate();
      } else if (
        Math.abs(touchStart.value.x - e.changedTouches[0].x) > TOUCH_SLOP ||
        Math.abs(touchStart.value.y - e.changedTouches[0].y) > TOUCH_SLOP
      ) {
        state.fail();
      }
    })
    .onUpdate(() => {
      console.log('pan update');
    });

  return (
    <GestureDetector gesture={gesture}>
      <View>{props.children}</View>
    </GestureDetector>
  );
};

const Main = () => {
  return (
    <ScrollView>
      <DragComponent>
        <View style={{ width: '100%', height: 400, backgroundColor: 'red' }} />
      </DragComponent>
      <View style={{ width: '100%', height: 1000, backgroundColor: 'blue' }} />
    </ScrollView>
  );
};
3reactions
ethansharcommented, Mar 17, 2022

Amazing! Thank you! I ended up combining your suggestion with my implementation.

I wanted to avoid implementing a LongPress behavior so I did the following and it works great!

const isDragging = useSharedValue(false);

const longPressGesture = Gesture.LongPress()
    .onStart(() => {
      isDragging.value = true;
    })
    .minDuration(250);


  const dragGesture = Gesture.Pan()
    .manualActivation(true)
    .onTouchesMove((_e, state) => {
      if (isDragging.value) {
        state.activate();
      } else {
        state.fail();
      }
    })
    .onStart(() => {...})
    .onUpdate(event => {...})
    .onEnd(() => {...})
    .onFinalize(() => {
      isDragging.value = false;
    })
    .simultaneousWithExternalGesture(longPressGesture);

  const composedGesture = Gesture.Race(dragGesture, longPressGesture);
   <GestureDetector gesture={composedGesture}>
    <View reanimated>{props.children}</View>
  </GestureDetector>
Read more comments on GitHub >

github_iconTop Results From Across the Web

Intercepting pan gestures over a UIScrollView breaks scrolling
1) Attach my own pan recognizer to the scroll view. · 2) Return YES on: – gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:.
Read more >
Pan Responder Inside of a ScrollView - Code Daily
You attemp to drag, all goes swimmingly, then the ScrollView scrolls, your drag stops working and it just sits there stuck until you...
Read more >
ScrollView from scratch with PanGestureHandler in ... - YouTube
In this tutorial we'll learn how to recreate the scroll behavior with just a PanGestureHandler component (from react-native- gesture -handler ...
Read more >
Advanced Scrollviews and Touch Handling Techniques
Highlighting objects · Pan Gesture Recognizer - for pan gestures · Pinch Gesture Recognizer - for pinch gestures · Touch Delay Gesture Recognizer...
Read more >
Longpress and list scrolling | Apple Developer Forums
The problem is that when I set "onLongPressGesture" anywhere in the list (on ... I can easily get a simple tap to work...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found