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
onLongPressfor 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-handlerfor 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
- Wrap your component in
GestureHandlerRootView - Use
PanGestureHandlerfor drag gestures - Track
translationXandtranslationYto update position - 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>