Skip to main content

Working with Touchables and Gestures

1. Using Pressable Component

The Pressable component in React Native is a versatile touchable component that provides a flexible way to handle user interactions such as taps, presses, and long presses. It allows you to customize the visual feedback based on the user's interaction state.

Property Description Values
onPress Callback function when the press is released Function
onLongPress Callback function when a long press is detected Function
onPressIn Callback function when the press is detected Function
onPressOut Callback function when the press is released Function
android_ripple Configuration for Android ripple effect Object
style Style object to customize the appearance Style object

Example: Basic Pressable Usage

import React from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';

const App = () => {
  return (
    <Pressable
      style={({ pressed }) => [
        styles.button,
        { backgroundColor: pressed ? '#0055ff' : '#007bff' }
      ]}
      onPress={() => console.log('Button pressed!')}
    >
      <Text style={styles.text}>Press Me</Text>
    </Pressable>
  );
};

const styles = StyleSheet.create({
  button: {
    padding: 10,
    margin: 10,
    borderRadius: 5,
  },
  text: {
    color: 'white',
    fontSize: 16,
  },
});

export default App;

Example: Using onPressIn and onPressOut

import React, { useState } from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';

const App = () => {
  const [isPressed, setIsPressed] = useState(false);

  return (
    <Pressable
      style={({ pressed }) => [
        styles.button,
        { backgroundColor: pressed ? '#ff5500' : '#ff7700' }
      ]}
      onPressIn={() => setIsPressed(true)}
      onPressOut={() => setIsPressed(false)}
    >
      <Text style={styles.text}>{isPressed ? 'Pressed!' : 'Release Me'}</Text>
    </Pressable>
  );
};

const styles = StyleSheet.create({
  button: {
    padding: 10,
    margin: 10,
    borderRadius: 5,
  },
  text: {
    color: 'white',
    fontSize: 16,
  },
});

export default App;

2. Understanding Touch Event System

Event Type Description Properties
onStartShouldSetResponder Called when a touch starts. Return true to become the responder. true or false
onMoveShouldSetResponder Called when a touch moves. Return true to become the responder. true or false
onResponderGrant Called when the touch responder is granted. Event object with locationX, locationY
onResponderMove Called when the touch moves while responder. Event object with locationX, locationY
onResponderRelease Called when the touch is released. Event object with locationX, locationY
onResponderTerminate Called when the touch is terminated by the system. Event object with locationX, locationY

Example: Basic Touch Event Handling

import React, { useState } from 'react';
import { View, Text } from 'react-native';

const TouchableExample = () => {
  const [touchCount, setTouchCount] = useState(0);

  const handlePress = () => {
    setTouchCount(touchCount + 1);
  };

  return (
    <View style={{ padding: 20 }}>
      <Text onPress={handlePress}>Press me! {touchCount} times</Text>
    </View>
  );
};

export default TouchableExample;
Note: Touch events in React Native are handled through the responder system, which manages touch handling and gesture recognition.

3. Implementing Long Press

Property Description Default Notes
onLongPress Callback function when long press is detected null Triggered after longPressDelay milliseconds
longPressDelay Delay in milliseconds before long press is recognized 500 Adjustable for different user needs

Example: Basic Long Press Implementation

<TouchableOpacity
  onLongPress={() => console.log('Long pressed!')}
  onPress={() => console.log('Short press')}
  style={styles.button}
>
  <Text>Press and Hold</Text>
</TouchableOpacity>
Note: The onLongPress event is not triggered on all platforms consistently. On iOS, it works as expected; on Android, it might require additional configuration or use of Pressable.
Warning: Avoid using onLongPress in combination with onPress without proper handling to prevent unintended actions.

Long Press Best Practices

  • Use onLongPress for actions that need confirmation or extended interaction.
  • Provide visual feedback when long press is detected (e.g., change background color).
  • Consider accessibility by ensuring the long press action is discoverable.

4. Handling Double Tap

Property Description Default
onDoubleTap Callback function when a double tap is detected null
delay Time in milliseconds between taps to count as a double tap 300

Example: Double Tap Handler

import React from 'react';
import { TouchableOpacity } from 'react-native';

const DoubleTapExample = () => {
  const handleDoubleTap = () => {
    console.log('Double tap detected');
  };

  return (
    <TouchableOpacity
      onDoubleTap={handleDoubleTap}
      style={{ padding: 20, backgroundColor: 'lightblue' }}
    >
      <Text>Double tap me</Text>
    </TouchableOpacity>
  );
};

export default DoubleTapExample;
Note: The onDoubleTap property is not a built-in React Native prop. You may need to implement it using onPress and tracking tap timing or use a third-party library like react-native-gesture-handler.

5. Creating Swipeable Components

Example: Basic Swipeable Component

import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { GestureHandlerRootView, Swipeable } from 'react-native-gesture-handler';

const SwipeableItem = ({ item, onSwipe }) => {
  const [isOpen, setIsOpen] = useState(false);

  const renderRightActions = (progress, dragX) => {
    const trans = dragX.interpolate({
      inputRange: [0, 50],
      outputRange: [0, 1],
    });

    return (
      <View style={styles.rightActions}>
        <Text style={styles.actionText}>Delete</Text>
      </View>
    );
  };

  return (
    <GestureHandlerRootView style={styles.container}>
      <Swipeable
        rightThreshold={50}
        renderRightActions={renderRightActions}
        onSwipeableOpen={() => setIsOpen(true)}
        onSwipeableClose={() => setIsOpen(false)}
      >
        <View style={styles.item}>
          <Text style={styles.text}>{item.title}</Text>
        </View>
      </Swipeable>
    </GestureHandlerRootView>
  );
};

const styles = StyleSheet.create({
  container: {
    marginVertical: 5,
  },
  item: {
    backgroundColor: '#f0f0f0',
    padding: 15,
    marginHorizontal: 10,
    borderRadius: 5,
  },
  text: {
    fontSize: 16,
  },
  rightActions: {
    backgroundColor: '#ff4d4d',
    justifyContent: 'center',
    alignItems: 'center',
    width: 80,
    height: '100%',
  },
  actionText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

export default SwipeableItem;
Property Description Default Notes
renderRightActions Function to render content on the right side of swipeable item null Required for right swipe actions
renderLeftActions Function to render content on the left side of swipeable item null Optional for left swipe actions
rightThreshold Distance in pixels to swipe before triggering right action 50 Adjust for sensitivity
leftThreshold Distance in pixels to swipe before triggering left action 50 Adjust for sensitivity
onSwipeableOpen Callback when swipeable item is opened null Use to track state or trigger animations
onSwipeableClose Callback when swipeable item is closed null Use to reset state or trigger animations
Note: Swipeable requires react-native-gesture-handler to be installed and linked properly.
Warning: Ensure GestureHandlerRootView wraps all gesture components for proper functionality.

Key Features of Swipeable Components

  • Supports swipe gestures in both directions (left and right)
  • Customizable actions for each side of the swipe
  • Can be used to implement delete, archive, or other actions
  • Works with react-native-gesture-handler for smooth animations

6. Implementing Drag and Drop

Example: Basic Drag and Drop Setup

import React, { useState } from 'react';
import { View, Text, PanGestureHandler, State } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

const DraggableItem = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const onGestureEvent = (event) => {
    setPosition({
      x: event.nativeEvent.translationX,
      y: event.nativeEvent.translationY
    });
  };

  const onHandlerStateChange = (event) => {
    if (event.nativeEvent.oldState === State.BEGAN) {
      // Handle drag start
    }
  };

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <PanGestureHandler
        onGestureEvent={onGestureEvent}
        onHandlerStateChange={onHandlerStateChange}>
        <View
          style={{
            width: 100,
            height: 100,
            backgroundColor: 'blue',
            position: 'absolute',
            left: position.x,
            top: position.y
          }}>
          <Text>Drag Me</Text>
        </View>
      </PanGestureHandler>
    </GestureHandlerRootView>
  );
};

export default DraggableItem;
Property Description Values Notes
onGestureEvent Callback for gesture updates function Called during drag movement
onHandlerStateChange Callback for gesture state changes function Handles drag start/end
translationX Horizontal translation from start number Used for positioning
translationY Vertical translation from start number Used for positioning
Note: Requires react-native-gesture-handler to be installed and linked.
Warning: Ensure GestureHandlerRootView wraps all gesture handlers for proper functionality.

Drag and Drop Workflow

  1. Wrap your component in GestureHandlerRootView
  2. Use PanGestureHandler for drag gestures
  3. Track translationX and translationY to update position
  4. Handle state changes for drag start/end logic

7. Using PanResponder API

The PanResponder API in React Native provides a low-level interface for handling complex touch gestures and multi-touch interactions. It allows you to define custom gesture recognition logic by specifying callbacks for various touch events.

Property Description Values
onMoveShouldSetPanResponder Called for every touch move while the gesture is active. Determines whether to start recognizing the gesture. Function returning boolean
onMoveShouldSetPanResponderCapture Same as above but called during the capture phase. Function returning boolean
onPanResponderReject Called when the gesture is rejected and not recognized. Function
onPanResponderRelease Called when the touch is released and the gesture ends. Function
onPanResponderStart Called when the gesture begins. Function
onPanResponderMove Called when the touch moves during the gesture. Function
onPanResponderEnd Called when the gesture ends (after release or rejection). Function

Example: Basic PanResponder Usage

import React, { PanResponder } from 'react-native';

const PanResponderExample = () => {
  const panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: (evt, gestureState) => {
      return gestureState.dx !== 0 || gestureState.dy !== 0;
    },
    onPanResponderMove: (evt, gestureState) => {
      // Handle movement
    },
    onPanResponderRelease: (evt, gestureState) => {
      // Handle release
    }
  });

  return (
    <View
      {...panResponder.panHandlers}
      style={{ width: 100, height: 100, backgroundColor: 'red' }}
    />
  );
};
Note: PanResponder is a powerful but low-level API. For simpler gestures, consider using GestureHandler from react-native-gesture-handler, which offers better performance and more features.

8. Handling Touch Feedback

Property Description Values
touchSoundDisabled Disables the touch sound on tap false (default), true
pressRetentionOffset Amount of space outside the view that still triggers a press { top: number, left: number, bottom: number, right: number }
hitSlop Defines the touchable area beyond the view bounds { top: number, left: number, bottom: number, right: number }

Example: Touch Feedback Configuration

import { TouchableOpacity } from 'react-native';

<TouchableOpacity
  touchSoundDisabled={true}
  pressRetentionOffset={{ top: 10, left: 10, bottom: 10, right: 10 }}
  hitSlop={{ top: 20, left: 20, bottom: 20, right: 20 }}
  onPress={() => console.log('Tapped!')}
>
  <View style={{ padding: 20, backgroundColor: 'lightblue' }}>
    <Text>Press Me</Text>
  </View>
</TouchableOpacity>
Note: hitSlop is useful for improving touch targets on small elements or when content is near the edge of a touchable area.

9. Implementing Gesture Recognition

Example: Basic Tap Gesture

import { TouchableOpacity } from 'react-native';

<TouchableOpacity
  onPress={() => console.log('Tapped!')}
  style={{ padding: 20, backgroundColor: '#f0f0f0' }}
>
  <Text>Press Me</Text>
</TouchableOpacity>
Gesture Props Description
onPress function Called when the touch is released
onLongPress function Called when the touch lasts longer than 500ms
onPressIn function Called when the touch begins
onPressOut function Called when the touch ends
Note: For more complex gestures, consider using react-native-gesture-handler.

Example: Using react-native-gesture-handler

import { Gesture, GestureDetector } from 'react-native-gesture-handler';

const tapGesture = Gesture.Tap()
  .onEnd((event) => {
    console.log('Tap recognized');
  });

<GestureDetector gesture={tapGesture}>
  <View style={{ padding: 20, backgroundColor: '#f0f0f0' }}>
    <Text>Tap Me</Text>
  </View>
</GestureDetector>

10. Using Touch Event Coordinates

Property Description Values
locationX X coordinate of the touch point relative to the view's origin number
locationY Y coordinate of the touch point relative to the view's origin number
pageX X coordinate of the touch point relative to the document number
pageY Y coordinate of the touch point relative to the document number

Example: Logging Touch Coordinates

<TouchableOpacity
  onPress={(e) => {
    console.log("Location X:", e.nativeEvent.locationX);
    console.log("Location Y:", e.nativeEvent.locationY);
    console.log("Page X:", e.nativeEvent.pageX);
    console.log("Page Y:", e.nativeEvent.pageY);
  }}
>
  <View style={{ width: 200, height: 200, backgroundColor: "lightblue" }}>
    <Text>Press Me</Text>
  </View>
</TouchableOpacity>

Was this article helpful?