Make TouchableOpacity not highlight element when starting to scroll [React Native]
Asked Answered
K

3

27

TouchableOpacity makes things touchable, or as React Native says:

A wrapper for making views respond properly to touches.

But using it inside a ScrollView or ListView results in highlighting when we (or at least I) do not want that.

These are three steps involved in scrolling down a ListView filled with elements:

  • Touch an element
  • Move finger up
  • Release finger

Touching the element immediately results in a highlight animation. But in this case, we just want to scroll. We do not want to do anything with that element, be it highlighting or opening a detail view etc.

This does not happen all the time but most of the times on my Android device.

What is a proper way of handling this?

Klug answered 3/6, 2016 at 9:28 Comment(2)
Out of interest, what's the use-case for wrapping a scroll-view in a touchable highlight?Tila
That's not the case. We are wrapping a TouchableOpacity element inside a ScrollView.Klug
T
40

A scroll gesture should cancel the TouchableOpacity touch responder, but if you think the TouchableOpacity highlight is triggered to early, you can try tweaking the delayPressIn property.

Torp answered 5/6, 2016 at 13:30 Comment(3)
Thank you. I did not consider the TouchableWithoutFeedback props. Now I set the delayPressIn to a very high number because I do not want that animation to appear when holding a button. It does not feel entirely right though. I think I am going to need a custom highlighting animation for tapping, holding and longPressing in the long run.Klug
Thanks delayPressIn worked well, I set mine to 50 which seems appropriate for my swipe gesture.Niela
Dude I love you so hard right now. I don't know why this prop. isn't documented. React-Native is a mess when it comes to documentation. Now I can stop using the buggy TouchableHighlight (which works really bad with borders and images)Roseliaroselin
F
5

You can use delayPressIn={1000}, which will delay the animation until you press for 1 second.

delayPressIn property of <TouchableOpacity> delay in ms, from the start of the touch, before onPressIn is called.

Example to use :

<FlatList
  horizontal
  contentContainerStyle={{ paddingRight: 16 }}   // this set the padding to last item
  showsHorizontalScrollIndicator={false}         // hide the scroller
  data={results}
  keyExtractor={(result) => result.data.id}
  renderItem={({ item }) => {
    return (
      <TouchableOpacity
        delayPressIn={1000}         // delay animation for 1 second
        onPress={() => navigation.navigate('ResultsShow')}
      >
        <ResultsDetail result={item.data} />
      </TouchableOpacity>
    );
  }}
/>;

You can find more about this Here.

Fawcette answered 3/6, 2016 at 9:28 Comment(0)
S
1

delayPressIn doesn't have the desired effect because, if the user taps fast enough, no effect is shown.

The right solution is use Pressable, which correctly avoids firing onPress when the user appears to be scrolling, and adding an opacity animation, like this:

const PressableWithAnimation = React.memo((props) => {
  const animated = useRef(new Animated.Value(1)).current;

  const handlePress = () => {
    animated.setValue(0.4);
    Animated.timing(animated, {
      toValue: 1,
      duration: 400,
      useNativeDriver: true,
    }).start()
    if (props.onPress) props.onPress();
  };

  return (
    <Pressable onPress={handlePress}>
      <Animated.View style={[styles.button, { opacity: animated }]}>
        {props.children}
      </Animated.View>
    </Pressable>
  );
});

You can compare all of the implementations I tried here: https://snack.expo.dev/@pickleheadsian/button-tests-in-scrollview

Subminiaturize answered 17/9, 2023 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.