4

I've got this code:

<View {...this.panGesture.panHandlers}>
    <Animated.View>

        <View style={{ position: 'absolute' }}>
            <TouchableHighlight onPress={ () => {console.log('hello')}>
            </TouchableHighlight>
        </View>

    </Animated.View>
</View>

and I can't for the love of mine get the onPress to work for iPhones 6s and above, when 3d touch is enabled.

I've tried most solutions suggested here, but with no luck.

I'd appreciate any help at all!

SudoPlz
  • 20,996
  • 12
  • 82
  • 123

2 Answers2

2

I have been able to workaround this specific issue by making sure the parent PanResponder doesn't grab the responder on move, until the touch has actually moved from the origin:

PanResponder.create({

  //... other responder callbacks

  onMoveShouldSetPanResponder(e, gestureState) {
    if (gestureState.dx === 0 || gestureState.dy === 0) {
      return false;
    }
    return true;
  }
});

This was back in around React Native 0.10 days, haven't tried it since. Hope it helps!

jevakallio
  • 35,324
  • 3
  • 105
  • 112
  • That doesn't seem to fix my problem. Of course I need to test on a device with 3d touch, but on NON 3d touch devices, I can see that `onMoveShouldSetPanResponder` is not called at all when I click the `TouchableHighlight`, therefore there's no point in adding code there..! This only get's called when I drag/move my finger on the screen and not on a single tap. Any ideas? – SudoPlz Feb 01 '17 at 15:57
  • This workaround fixed an issue that only triggers on 3d devices. I can't guarantee that it will work, but I think you should try it! – jevakallio Feb 01 '17 at 16:32
0

Ok after fiddling a lot with this, I realised the solution was partly what jevakallio said BUT I can no longer use Touchables when 3D touch is enabled so I have to reproduce their behaviour manually.

My code now looks like this: The child code:

let touchable;

if (View.forceTouchAvailable === false) { // if 3d touch is disabled
    // just use a Touchable and all should be fine.
    touchable = (<TouchableHighlight
        onPress={ () => {console.log('hello world');}
        style={this.props.style}
        underlayColor={this.props.highlightBgColor}
    >
        {this.props.children}
    </TouchableHighlight>);
} else { // Otherwise if 3D touch is enabled
    // we'll have to use a child view WITH a PanResponder
    touchable = (<View
      style={[this.props.style, {
        backgroundColor: this.state.panViewIsTapped ?
          this.props.highlightBgColor
          :
          bgColor,
      }]}
      {...this.panResponderChild.panHandlers}
    >
      {this.props.children}
    </View>);
}

return (<View {...this.panResponderParent.panHandlers}>
    <Animated.View>

        <View style={{ position: 'absolute' }}>
            {touchable}
        </View>

    </Animated.View>
</View>);

The child pan responder code (this.panResponderChild):

this.panResponderChild = PanResponder.create({

        // true because we want tapping on this to set it as the responder
        onStartShouldSetPanResponder: () => true,

        // true because we want this to capture the responder lock from it's parent on start
        onStartShouldSetPanResponderCapture: () => true,

        // when the pan responder lock is terminated, set the pan view as NOT tapped
        onPanResponderTerminate: () => {
          this.setState({ panViewIsTapped: false });
        },

        // true so that the parent can grab our responder lock if he wan'ts to.
        onPanResponderTerminationRequest: () => true,

        // false because we DON'T want this btn capturing the resp lock from it's parent on move
        onMoveShouldSetPanResponderCapture: () => false,

        // false because we DON'T want moving the finger on this to set it as the responder
        onMoveShouldSetPanResponder: () => false,

        onPanResponderGrant: () => {
          this.setState({ panViewIsTapped: true });
        },

        onPanResponderRelease: () => {
          this.setState({ panViewIsTapped: false });
          console.log('hello world');
        },
})

The parent pan responder code (this.panResponderParent):

this.panResponderParent = PanResponder.create({

      // true because we want tapping on the cal, to set it as a responder
      onStartShouldSetPanResponder: () => true,

      // false because we DON'T want to grab the responder lock from our children on start
      onStartShouldSetPanResponderCapture: () => false,

      /*
      onMoveShouldSetPanResponderCapture:
        false because we don't want to accidentally grab the responder lock from
        our children on movement.
          That's because sometimes even a small tap contains movement,
          and thus a big percentage of taps will not work.
          Keeping that flag false does not nessecarily mean that our children will
         always capture the responder lock on movement, (they can if they want),
         we're just not strict enough to grab it from them.
      */
      onMoveShouldSetPanResponderCapture: () => false,

      /*
      onMoveShouldSetPanResponder:
        We DO want moving the finter on the cal, to set it as a responder,
        BUT, we don't always want moving the finger on an appointment setting this parent
        as the responder.

        Logic:
        So, when the dx AND dy of the pan are 0, we return false, because we don't
        want to grab the responder from our appointment children.

        For anything other than that we just allow this parent to become the responder.

        (dx, dy: accumulated distance of the gesture since the touch started)
      */
      onMoveShouldSetPanResponder: (e, gestureState) =>
        !(gestureState.dx === 0 || gestureState.dy === 0),


    });
SudoPlz
  • 20,996
  • 12
  • 82
  • 123