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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Press Events not fired for Svg elements inside a PanResponder #363

Closed
chrisbolin opened this issue Jun 12, 2017 · 8 comments
Closed
Labels

Comments

@chrisbolin
Copy link

Hey there! We use react-native-svg for Victory's native support, and for the most part it's worked wonderfully. 馃挴 One of our community members found an issue with Android, however. (Reproduced on the latest version of react-native-svg, 5.2.0)

The press events for SVG elements don't work if the element is inside a PanResponder. This only affects Android; iOS is fine. RN's Button and Touchables work fine in a PanResponder. Quick reproduction:

jun-12-2017 18-22-22 - react-native bug

Entire code need for reproduction:

import React, { Component } from "react";
import { PanResponder, View, Text, Button } from "react-native";
import { Svg, Rect, Text as SvgText } from "react-native-svg";

class PanResponderWrapper extends Component {
  constructor(props) {
    super(props);
    this.panResponder = this.getResponder();
  }
  getResponder() {
    const yes = () => true;
    const no = () => false;
    const noop = () => null;
    return PanResponder.create({
      onStartShouldSetPanResponder: yes,
      onStartShouldSetPanResponderCapture: no,
      onMoveShouldSetPanResponder: yes,
      onMoveShouldSetPanResponderCapture: yes,
      onShouldBlockNativeResponder: yes,
      onPanResponderTerminationRequest: yes,
      onPanResponderGrant: noop,
      onPanResponderMove: noop,
      onPanResponderRelease: noop,
      onPanResponderTerminate: noop,
    });
  }
  render() {
    return (
      <View {...this.panResponder.panHandlers}>
        {this.props.children}
      </View>
    );
  }
}

const Box = ({ onPressIn, label }) => (
  <Svg width={400} height={100} viewBox="0 0 400 100" style={{ width: "100%", paddingTop: 10 }}>
    <Rect onPressIn={onPressIn} fill="lightblue" x="10" y="10" width="350" height="90" />
    <SvgText x="30" y="50">{label}</SvgText>
  </Svg>
);

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  countUp() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    return (
      <View style={{ paddingTop: 30 }}>
        <Box label="Rect" onPressIn={() => this.countUp()} />

        <PanResponderWrapper>
          <Button
            onPress={() => this.countUp()}
            title="panResponder > Button"
          />
        </PanResponderWrapper>

        <PanResponderWrapper>
          <Box label="panResponder > Rect" onPressIn={() => this.countUp()} />
        </PanResponderWrapper>

        <Text style={{fontSize: 20, textAlign: "center", padding: 30}}>{this.state.count}</Text>
      </View>
    );
  }
}
@chrisbolin
Copy link
Author

https://github.com/FormidableLabs/victory-native-demo/tree/vn-96-reproduction is a Expo app that shows the behavior. But I've also reproduced it with raw react-native@0.45.1 + react-native-svg@^5.2.0

@hasLandon
Copy link

I'm starting to think this is a bug in react native itself. If you look, there's all kinds of reported issues with PanResponder on android. Also, I have been inspecting all the touch events that RNSvg is dispatching to react native, and they're identical for both the rect outside the PanResponder and the rect inside the PanResponder.

@CherishIt
Copy link

I encountered the same problem. Any update on this issue?

@SethHamilton
Copy link

I have posted a fix/workaround here:

https://github.com/FormidableLabs/victory-native/issues/25

@msand
Copy link
Collaborator

msand commented Sep 23, 2018

@chrisbolin I think I've fixed the issue, could you try with this and the latest commit in the master branch?

import React, { Component } from 'react';
import { PanResponder, View, Text, Button } from 'react-native';
import { Svg, Rect, Text as SvgText } from 'react-native-svg';

class PanResponderWrapper extends Component {
  constructor(props) {
    super(props);
    this.panResponder = this.getResponder();
  }
  getResponder() {
    const yes = () => true;
    const no = () => false;
    const noop = () => null;
    return PanResponder.create({
      onStartShouldSetPanResponder: yes,
      onStartShouldSetPanResponderCapture: no,
      onMoveShouldSetPanResponder: yes,
      onMoveShouldSetPanResponderCapture: yes,
      onShouldBlockNativeResponder: yes,
      onPanResponderTerminationRequest: yes,
      onPanResponderGrant: noop,
      onPanResponderMove: noop,
      onPanResponderRelease: noop,
      onPanResponderTerminate: noop,
    });
  }
  render() {
    return (
      <View {...this.panResponder.panHandlers}>{this.props.children}</View>
    );
  }
}

const Box = ({ onPress, onPressIn, onPressOut, label }) => (
  <Svg
    width={400}
    height={100}
    viewBox="0 0 400 100"
    style={{ width: '100%', height: 100, paddingTop: 10 }}
  >
    <Rect
      onPress={onPress}
      onPressIn={onPressIn}
      onPressOut={onPressOut}
      fill="lightblue"
      x="10"
      y="10"
      width="350"
      height="90"
      rejectResponderTermination
    />
    <SvgText x="30" y="50">
      {label}
    </SvgText>
  </Svg>
);

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  countUp(source) {
    //console.log(source);
    this.setState(({ count }) => ({ count: count + 1 }));
  }
  render() {
    return (
      <View style={{ paddingTop: 30 }}>
        <Box
          label="Rect"
          onPress={() => this.countUp('press')}
          onPressIn={() => this.countUp('pressin')}
          onPressOut={() => this.countUp('pressout')}
        />

        <PanResponderWrapper>
          <Button
            onPress={() => this.countUp('button')}
            title="panResponder > Button"
          />
        </PanResponderWrapper>

        <PanResponderWrapper>
          <Box
            label="panResponder > Rect"
            onPress={() => this.countUp('press')}
            onPressIn={() => this.countUp('pressin')}
            onPressOut={() => this.countUp('pressout')}
          />
        </PanResponderWrapper>

        <Text style={{ fontSize: 20, textAlign: 'center', padding: 30 }}>
          {this.state.count}
        </Text>
      </View>
    );
  }
}

N.B. The Rect element has the rejectResponderTermination attribute set. As per the source code comments of Touchable:
https://github.com/facebook/react-native/blob/0.57-stable/Libraries/Components/Touchable/Touchable.js#L368

  // ==== Hooks to Gesture Responder system ====
  /**
   * Must return true if embedded in a native platform scroll view.
   */
  touchableHandleResponderTerminationRequest: function() {
    return !this.props.rejectResponderTermination;
  },

Otherwise, on Android, the onPress/onPressOut handlers don't get called. As the PanResponder becomes the responder. But, now onPressIn gets called anyway at least. On iOS it seems to work for all three events even without the rejectResponderTermination attribute. I think it will probably disable scrolling if the Rect element becomes the Responder and the rejectResponderTermination attribute is set, but haven't tested it extensively yet.

@msand
Copy link
Collaborator

msand commented Dec 10, 2018

This seems to work correctly on both iOS and Android with the latest version. Closing.

@msand msand closed this as completed Dec 10, 2018
@msand msand removed the help wanted label Jan 30, 2019
@robertcreaner
Copy link

Hi @msand the issue is still occuring for me with the following component structure:
PinchGestureHandler -> PanGestureHander -> Animated.View -> Svg

onPress seems to work fine when the scale is 1 but after zooming in the onPress becomes very unpredictable and only works half the time

@pkvov
Copy link

pkvov commented Jun 25, 2021

Hi @msand the issue is still occuring for me with the following component structure:
PinchGestureHandler -> PanGestureHander -> Animated.View -> Svg

onPress seems to work fine when the scale is 1 but after zooming in the onPress becomes very unpredictable and only works half the time

have the same problem when i use ZoomableSvg, when zoom less than 1, the child Svg Element' onPress not fired

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants