software-mansion/react-native-gesture-handler

Tap gesture with multiple pointers fail to start on `2.15.0`

levibuzolic opened this issue · 7 comments

Description

Upgrading from 2.14.1 to 2.15.0 has stopped tap gestures with minPointers(2) from firing.

The tap gesture is setup as following:

const tap = Gesture.Tap()
  .minPointers(2)
  .runOnJS(true)
  .onBegin((event) => {
    console.log('onBegin', JSON.stringify({ event }));
  })
  .onStart((event) => {
    console.log('onStart', JSON.stringify({ event }));
  })
  .onEnd((event, success) => {
    console.log('onEnd', { event, success });
  });
  • On 2.15.0 only onBegin fires, no other event is triggered.
  • On 2.14.1 all 3 events are triggered as expected.
  • Switching to minPointers(1), the gesture works as expected

Minimal reproduction

https://github.com/levibuzolic/react-native-gesture-handler/blob/main/example/src/new_api/twoPointerTap/index.tsx

import React, { useState } from 'react';
import { Text, StyleSheet, View, ScrollView, Button } from 'react-native';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';

export default function Home() {
  const [log, setLog] = useState<string[]>([]);

  const tap = Gesture.Tap()
    .minPointers(1)
    .runOnJS(true)
    .onBegin((event) => {
      setLog((prev) => [`onBegin: ${JSON.stringify({ event })}`, ...prev]);
    })
    .onStart((event) => {
      setLog((prev) => [`onStart: ${JSON.stringify({ event })}`, ...prev]);
    })
    .onEnd((event, success) => {
      setLog((prev) => [
        `onEnd: ${JSON.stringify({ event, success })}`,
        ...prev,
      ]);
    });

  return (
    <View style={styles.container}>
      <GestureDetector gesture={tap}>
        <View style={styles.container}>
          <ScrollView>
            <Button title="Reset" onPress={() => setLog([])} />
            {log.map((line, i) => (
              <Text key={i} style={[styles.log, i === 0 && styles.logNew]}>
                {line}
              </Text>
            ))}
          </ScrollView>
        </View>
      </GestureDetector>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    width: '100%',
    height: '100%',
    backgroundColor: 'white',
  },
  log: {
    fontSize: 12,
    color: 'grey',
    padding: 5,
  },
  logNew: {
    backgroundColor: '#e6ffec',
    color: 'black',
  },
});

Video

Kapture.2024-02-09.at.17.07.38.mp4

Steps to reproduce

  1. Set up a Gesture.Tap() with .minPointers(2)
  2. Perform a 2 pointer tap
  3. onStart and onEnd are not fired

Snack or a link to a repository

https://github.com/software-mansion/react-native-gesture-handler

See the TwoPointerTap example.

Gesture Handler version

2.15.0

React Native version

0.72.0

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

I'm in the process of looking through the diff between 2.14.1 and 2.15.0 to see if I can spot anything related.

Adding onFinalize to the events, I can see that's getting called with success: false.

const tap = Gesture.Tap()
  .minPointers(2)
  .runOnJS(true)
  .onBegin((event) => {
    setLog((prev) => [`onBegin: ${JSON.stringify({ event })}`, ...prev]);
  })
  .onStart((event) => {
    setLog((prev) => [`onStart: ${JSON.stringify({ event })}`, ...prev]);
  })
  .onEnd((event, success) => {
    setLog((prev) => [
      `onEnd: ${JSON.stringify({ event, success })}`,
      ...prev,
    ]);
  })
  .onFinalize((event, success) => {
    setLog((prev) => [
      `onFinalize: ${JSON.stringify({ event, success })}`,
      ...prev,
    ]);
  });

Looks like theres a bit of a diff in the tap handler for macOS support, is suepct it's something there but haven't found the smoking gun yet. 👀

Hi @levibuzolic! I will take a look at it and get back to you as soon as I find something!

Okay, I've found out what causes this problem. Before macOS support NSSet touches contained UITouch elements and calling count resulted in getting actual amount of pointers. Now this set contains only one element and the real number of touches is hidden inside this element.

We will prepare a PR to fix that soon!

@m-bert awesome, thanks for looking into this! 🙇‍♂️

I've prepared PR that should fix this problem and I'd be happy if you could check if it helps!