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

Fixed faulty pinch gesture handler on Android when in a ScrollView/FlatList #2370

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

timbrandin
Copy link

Description

FYI this is the updated version of a previous PR for v1.6.

I noticed that ScaleGestureDetector returned zero and different values for scale and focal point as the iOS equivalent, so I suggest this solution which uses the bounding box of the touches to calculate the scale from the difference between the hypothenuse from initial touch. Similar to how PanGestureHandler calculates translation using start difference.

Also with this we get focal point in the local coordinate system of the handler view, calculated using average position of all touches. Which is what you would expect from iOS.

Before fix:
https://user-images.githubusercontent.com/1715834/211769742-15890fb3-4338-435d-86a3-5e997cd47cdb.mp4

After fix:
https://user-images.githubusercontent.com/1715834/211769926-9b462322-29ca-460d-b071-3ab0b4a48577.mp4

Test plan

This code has also been in production since 2021, have tested for our use case on most common android devices and versions.

m-bert added a commit that referenced this pull request Feb 5, 2024
## Description

While investigating #2370 I've found out that if handler has `enabled: false` and moves to state `END`, it won't reach `UNDETERMINED`. This fact makes it useless because it is stuck at finished state. It happens because of [this line](https://github.com/software-mansion/react-native-gesture-handler/blob/a30f42f3fe5f08e9746864a7e6f7a15d15936a0f/src/web/tools/GestureHandlerOrchestrator.ts#L118). In order to fix that, I've decided to reset handler in case if it moves to `END` state while being inactive.

Fixes #2730

## Test plan

<details>
<summary>Tested on the following code</summary>

```jsx
import React, { useState } from 'react';
import {
  View,
  SafeAreaView,
  Text,
  TextInput,
  TouchableOpacity as RNTouchable,
} from 'react-native';
import { TouchableOpacity as GHTouchable } from 'react-native-gesture-handler';

const BrokenGestureHandlerTouchable = () => {
  const [disabled, setDisabled] = useState(true);
  const [didClick, setDidClick] = useState(false);
  return (
    <View>
      <Text>
        Breaks with react-native-gesture-handler's TouchableOpacity. Refresh the
        page, click the disabled button, edit the text, click the button that
        should be enabled again and the click isn't triggered (no opacity change
        and no function call). If you refresh the page, edit the text, then
        click the enabled button, it works as expected.
      </Text>
      <TextInput
        placeholder="Text Input"
        placeholderTextColor="#AAAAAA"
        onChangeText={(text) => {
          setDisabled(false);
        }}
      />
      <GHTouchable
        disabled={disabled}
        onPress={() => {
          setDidClick(true);
        }}
        style={{
          borderWidth: 1,
          borderColor: 'pink',
        }}>
        <Text>Click me! {`Disabled: ${disabled}. Clicked: ${didClick}`}</Text>
      </GHTouchable>
    </View>
  );
};

const WorkingReactNativeTouchable = () => {
  const [disabled, setDisabled] = useState(true);
  const [didClick, setDidClick] = useState(false);
  return (
    <View>
      <Text>
        Works with react-native's TouchableOpacity. Refresh the page, click the
        disabled button, edit the text, click the now-enabled button again and
        it works.
      </Text>
      <TextInput
        placeholder="Text Input"
        placeholderTextColor="#AAAAAA"
        onChangeText={(text) => {
          setDisabled(false);
        }}
      />
      <RNTouchable
        disabled={disabled}
        onPress={() => {
          setDidClick(true);
        }}
        style={{
          borderWidth: 1,
          borderColor: 'pink',
        }}>
        <Text>Click me! {`Disabled: ${disabled}. Clicked: ${didClick}`}</Text>
      </RNTouchable>
    </View>
  );
};

export default function App() {
  return (
    <SafeAreaView>
      <WorkingReactNativeTouchable />
      <View
        style={{
          height: 50,
        }}
      />
      <BrokenGestureHandlerTouchable />
    </SafeAreaView>
  );
}

```

</details>
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.

1 participant