Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Animated.Value as enabled prop on gesture handler #900

Closed
terrysahaidak opened this issue Dec 30, 2019 · 10 comments
Closed

Use Animated.Value as enabled prop on gesture handler #900

terrysahaidak opened this issue Dec 30, 2019 · 10 comments
Labels
Area: Gesture handlers This issue is related to problems with gesture handlers BugBash 31.03 Close when stale The issue will be closed automatically if it remains inactive

Comments

@terrysahaidak
Copy link

terrysahaidak commented Dec 30, 2019

There are lots of issues using PanGestureHandler with ScrollView, for example this one. Not sure it's something we can easily fix, but I think it's something we can workaround by using animated values.

Not everyone knows, you can createAnimatedComponent of almost every component and then use Animated.Values (for example with reanimated) to animate some of the props. That's pretty the same as using setNativeProps, but instead of calling it from js, you can pass the animated value to the animated component and change it from the animation definition (in reanimated).

Using this we can workaround it by disabling scroll when gesture handler should handle the touch and disable gesture handler when only scroll should handle the touch.

Not quite sure gesture handler and scroll view both concel its touches before switching it, hope so.

You can see POC using scrollEnabled as Animated Prop for Bottom Sheet component here (seems to be working on iOS only for now):
https://snack.expo.io/@terrysahaidak/reanimated-scroll-enabled

/cc @kmagiera @osdnk

@ianmartorell
Copy link

I was trying to achieve the same thing with the enabled prop of PanGestureHandler, but it seems you can't use createAnimatedComponent on a gesture handler? It stopped reacting to events at all for me.

I ended up doing it like this, but it would be nice if it could be done without crossing the native bridge:

const [panGestureHandlerEnabled, setPanGestureHandlerEnabled] = useState(false);

useCode(
  () =>
    onChange(
      dragEnabled,
      call([dragEnabled], ([dragEnabled]) =>
        setPanGestureHandlerEnabled(!!dragEnabled)
      )
    ),
  [dragEnabled, setPanGestureHandlerEnabled]
);

@kulikalov
Copy link

@terrysahaidak thanks for your post! This was really helpful. Dear maintainers, your lib is awesome, but the docs are complete trash. Please, take some time to improve it. I had to figure this out bit by bit through all the docs, various tutorials, and this library source code.

@a-eid
Copy link

a-eid commented Aug 2, 2020

@kulikalov were you able to resolve this ?

@a-eid
Copy link

a-eid commented Aug 2, 2020

actually wait, wrapping PanGestureHandler inside a Animated.View and playing with pointerEvents might work.

@a-eid
Copy link

a-eid commented Aug 2, 2020

wasn't able to do what I wanted !!

@a-eid
Copy link

a-eid commented Aug 2, 2020

@ianmartorell this would trigger a rerender for all it's children.

@a-eid
Copy link

a-eid commented Aug 3, 2020

this might work

<PanGestureHandler>
  <A.View style={...} pointerEvents={pointerEvents}>
....

pointerEvent could possibly be an animated node and could disable / enable the PanGesureHandler component.
A.Node<"none" | "auto" | "box-none" | "box-only"

this might not be enough but it's what I was looking for in this case.

@jakub-gonet jakub-gonet added the Area: Gesture handlers This issue is related to problems with gesture handlers label Sep 3, 2020
@ShaMan123
Copy link
Contributor

#1034

@terrysahaidak
Copy link
Author

#1034

I don't think it's related.

@j-piasecki j-piasecki added Close when stale The issue will be closed automatically if it remains inactive BugBash 31.03 labels Mar 31, 2022
@j-piasecki
Copy link
Member

Hi! touch events and manualActivation introduced in Gesture Handler 2.0 may be enough to accomplish this behavior. Here's a simple example on how to use them: https://docs.swmansion.com/react-native-gesture-handler/docs/manual-gestures/manual-gestures/.

j-piasecki added a commit that referenced this issue Jan 9, 2023
…2092)

## Description

`GestureDetector` was not reattaching gestures if the underlying view
has changed, which was especially noticeable when using layout
animations.
This PR updates `GestureDetector` to keep track of the tag of the view
it's attached to and to reattach gestures it the tag changes.

The second commit also fixes gestures not reattaching when manually
changing the underlying view (at the expense of forcing another render),
but only when Reanimated is not used.
Applying the following patch:
<details>
<summary>Expand</summary>

```diff
diff --git a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
index 1cf0c3f..3f22437 100644
--- a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
+++ b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
@@ -294,19 +294,12 @@ export default function createAnimatedComponent(
       const node = this._getEventViewRef();
       const attached = new Set();
       const nextEvts = new Set();
-      let viewTag: number | undefined;
+      let viewTag: number | undefined =  RNRenderer.findHostInstance_DEPRECATED(this)._nativeTag;
 
       for (const key in this.props) {
         const prop = this.props[key];
         if (prop instanceof AnimatedEvent) {
           nextEvts.add((prop as AnimatedEvent).__nodeID);
-        } else if (
-          has('current', prop) &&
-          prop.current instanceof WorkletEventHandler
-        ) {
-          if (viewTag === undefined) {
-            viewTag = prop.current.viewTag;
-          }
         }
       }
       for (const key in prevProps) {

```

</details>
also makes it work when using Reanimated, but I'm not sure whether it's
fine to change it this way upstream. This needs to be discussed.

## Test plan

Tested on the Example app and on the following code:

<details>
<summary>Expand</summary>

```jsx
import React, { useState } from 'react';
import { Text, View } from 'react-native';
import {
  FlatList,
  Gesture,
  GestureDetector,
} from 'react-native-gesture-handler';
import Animated, { BounceIn } from 'react-native-reanimated';

const items = [
  { name: 'Item A' },
  { name: 'Item B' },
  { name: 'Item C' },
  { name: 'Item D' },
  { name: 'Item A' },
  { name: 'Item B' },
  { name: 'Item C' },
  { name: 'Item D' },
  { name: 'Item A' },
  { name: 'Item B' },
  { name: 'Item C' },
  { name: 'Item D' },
  { name: 'Item A' },
  { name: 'Item B' },
  { name: 'Item C' },
  { name: 'Item D' },
  { name: 'Item A' },
  { name: 'Item B' },
  { name: 'Item C' },
  { name: 'Item D' },
];

function Item() {
  const [faved, setFaved] = useState(false);
  const color = faved ? '#900' : '#aaa';
  const tap = Gesture.Tap()
    .onEnd(() => {
      setFaved(!faved);
    })
    .runOnJS(true);

  return (
    <GestureDetector gesture={tap}>
      <Animated.View
        key={color}
        entering={BounceIn}
        style={{ backgroundColor: color, width: 30, height: 30 }}
      />
    </GestureDetector>
  );
}

function renderItem({ item }: { item: { name: string } }) {
  return (
    <View
      style={{
        width: '100%',
        height: 50,
        backgroundColor: 'red',
        flexDirection: 'row',
        justifyContent: 'space-between',
        padding: 10,
        alignItems: 'center',
      }}>
      <Text>{item.name}</Text>
      <Item />
    </View>
  );
}

export default function Example() {
  return (
    <View style={{ flex: 1 }}>
      <FlatList style={{ flex: 1 }} data={items} renderItem={renderItem} />
    </View>
  );
}

```

</details>

Code to test the second commit:

<details>
<summary>Expand</summary>

```jsx
import React from 'react';
import { View } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';

function Item() {
  console.log('render item');
  return (
    <Animated.View
      style={{
        alignSelf: 'center',
        width: 200,
        height: 200,
        backgroundColor: 'red',
      }}
    />
  );
}

export default function Example() {
  const gesture = Gesture.Tap()
    .onStart(() => {
      console.log('a', _WORKLET);
    })
    .runOnJS(true);
  console.log('render parent');
  return (
    <View style={{ flex: 1 }}>
      <GestureDetector gesture={gesture}>
        <Item />
      </GestureDetector>
    </View>
  );
}
```

Change between `View` and `Animated.View` while the app is running and
check if the tap still works. Remove `.runOnJS(true)` to test using
Reanimated.

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Gesture handlers This issue is related to problems with gesture handlers BugBash 31.03 Close when stale The issue will be closed automatically if it remains inactive
Projects
None yet
Development

No branches or pull requests

7 participants