How to pass onPress in a map function to a custom component? - react-native

I have a icons.js which consist of an array of objects:
const icons =[
{ name: 'Camera', image: <Icon name='device-camera' size={70} />, onPress: pickSingleWithCamera},
{ name: 'Earth', image: <SimpleLineIcons name='camera' size={70} />, onPress: 'bye' }
]
picksinglWithCamera is a function:
const pickSingleWithCamera = () => {
ImagePicker.openCamera({
cropping: true,
width: 500,
height: 500,
}).then(image => {
console.log('received image', image);
this.setState({
image: {uri: image.path, width: image.width, height: image.height},
images: null
});
}).catch(e => alert(e));
}
Now I have the main component in main.js which imports both the icons.js and title.js files.
The title component:
<Title
text={ focused ? focused.name : '' }
data={this.cells}
top={(SIZE + GUTTER) * 2}
visibility={this.text}
/>
What I am trying to do here is whenever user presses the text (name attribute of array icons), the respective function of it is called.
Unfortunately I am unsuccessful doing that, that's why I came onto SO to ask.
import React, { Component } from 'react'
import { Animated, Text, TouchableOpacity, Alert, View } from 'react-native'
import { HIDDEN, VISIBLE } from './animation-state'
export default class Title extends Component {
constructor(props) {
super(props)
this.state = {}
}
action(text, top, visibility, data) {
return data.map((cell, i) => {
return (
<Animated.View
key={i}
style={{
position: 'absolute',
top,
left: 0,
right: 0,
opacity: visibility.interpolate({
inputRange: [HIDDEN, VISIBLE],
outputRange: [0, 1],
}),
backgroundColor: 'transparent',
transform: [
{
translateY: visibility.interpolate({
inputRange: [HIDDEN, VISIBLE],
outputRange: [100, 0],
}),
},
],
}}
>
<TouchableOpacity onPress={() => cell.onPress}>
<Text
style={{
fontSize: 40,
fontFamily: 'GillSans-SemiBold',
textAlign: 'center',
}}
>
{text}
</Text>
</TouchableOpacity>
</Animated.View>
)
})
}
render() {
const { text, top, visibility, data } = this.props
return (
<View>
{this.action(text, top, visibility, data)}
</View>
)
}
}

Arrow functions have immediate returns, in your case what you are doing translates to the following:
onPress={function(){return cell.onPress;}}
This will do nothing because you are not executing the function, you have to tell the function to execute
onPress={() => cell.onPress()}
Another solution would be to just assign the function without arrow functions, this has the downside that onPress won't be binded to the component
onPress={cell.onPress}

Related

Changed from react native class to function and it doesnt work 100%

I am trying to change a class into a function. This is mainly because if I can get it working, I want to use it for learning different animations, I have had some success but not 100%. Originally it displayed an icon that when clicked it spun it one way and then when clicked again it spun the other way. What I have tried to do it get rid of the icon and replace it with an image. It works when clicked once but then does nothing.
I am struggling with toggled aspect of it and setting the state I think because I cant seem to set it up properly in a function.
I have tried several things but this is the best I can get. If I show the original code and then what I have managed to change, maybe someone can point me in the right direction as to what I am doing wrong.
All I want is the image to display and then when clicked spins right and then if clicked again it spins left.
I am doing this so I can mess around with the settings and hopefully learn animation a bit better.
Any help would be greatly appreciated.
The original code :
import React from 'react';
import { View, StyleSheet, Animated, Image, TouchableOpacity } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
const TabIcon = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
if (menuToggled !== null) {
const animation = new Animated.Value(menuToggled ? 0 : 1);
Animated.timing(animation, {
toValue: menuToggled ? 1 : 0,
duration: 500,
useNativeDriver: true
}).start();
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
}
return (
<TouchableOpacity
style={styles.tabStyle}
onPress={onPress}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
export default class App extends React.Component {
state = {
menuToggled: null
}
toggleMenu = () => {
this.setState(prevState => {
return { menuToggled: !prevState.menuToggled };
});
}
render () {
return (
<View style={styles.container}>
<TabIcon
onPress={this.toggleMenu}
menuToggled={this.state.menuToggled}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
and what I have changed so far :
import React, { useRef, useState } from "react";
import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState([null]);
toggleMenu = () => {
setMenuToggled(menuToggled === "null" ? "menuToggled" : "null");
}
const Spinner = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
const animation = new Animated.Value(0);
const go = () => {
Animated.timing(animation, {
toValue: 1,
duration: 1500,
easing: Easing.elastic(1),
useNativeDriver: true
}).start();
}
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={go}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<Spinner
onPress={toggleMenu}
menuToggled={menuToggled}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;
There are a few issues. You first had menuToggled initialized to [null] when it should have been null. You also had forgotten to use onPress in TabIcon. The most noteworthy thing was wrapping TabIcon in a useCallback to prevent it from being recreated all the time. Expo snack:
import React, { useRef, useState, useCallback } from 'react';
import {
View,
StyleSheet,
Animated,
Image,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState(null);
const TabIcon = useCallback(({ onPress, menuToggled }) => {
const logoStyles = [styles.logoStyle];
// initialized base on menuToggled
// if not done then it will take an additional button press to trigger
// the animation
const animation = useRef(new Animated.Value(menuToggled ? 0 : 1)).current;
const startAnimation = () => {
Animated.timing(animation, {
toValue: menuToggled ? 1 :0,
duration: 500,
useNativeDriver: true,
}).start();
};
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={() => {
startAnimation();
onPress?.();
}}>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
</Animated.View>
</TouchableOpacity>
);
},[]);
return (
<View style={styles.container}>
<TabIcon
onPress={() => setMenuToggled((prev) => !prev)}
menuToggled={menuToggled}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;

How to remove/replace default Profile Picture with Image Picker In React Native Expo

I am looking for assistance on how to replace a default profile picture with the one a user would select from their media library.
I have managed to create an onPress function that allows the user to select the image from their media library. The image is returned and displayed also in the prescribed layout.
My problem is that I cannot see the default profile picture, but I can see and click on a pencil icon to prompt the image-picker for iOS and Android as I am using Expo.
Here is my custom component code:
import React, { useState, useEffect } from "react";
import {
StyleSheet,
View,
Text,
Image,
TouchableOpacity,
useWindowDimensions,
Platform,
} from "react-native";
import { Controller } from "react-hook-form";
import * as ImagePicker from "expo-image-picker";
import { Ionicons } from "#expo/vector-icons";
//import dependencies
import { COLORS, SIZES, images } from "../constants";
const CustomImagePicker = ({ control, name, rules = {} }) => {
const { height } = useWindowDimensions();
const [hasGalleryPermission, setHasGalleryPermission] = useState("false");
const [profilePicture, setProfilePicture] = useState(name);
useEffect(() => {
async () => {
const galleryStatus =
await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasGalleryPermission(galleryStatus.status === "granted");
};
}, []);
const pickImage = async () => {
let chosenImage = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(JSON.stringify(chosenImage));
if (!chosenImage.cancelled) {
setProfilePicture(chosenImage.uri);
}
};
if (hasGalleryPermission === false) {
return <Text>❌ No access to Internal Storage</Text>;
}
return (
<Controller
name={name}
control={control}
rules={rules}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<>
<View
style={[
styles.container,
{ borderColor: error ? COLORS.red : COLORS.gray },
]}
>
<TouchableOpacity
style={styles.touchPicture}
onPress={() => pickImage()}
>
<Ionicons name="pencil-outline" size={24} color={COLORS.white} />
</TouchableOpacity>
<Image
onChange={onChange}
value={value}
source={{
uri: profilePicture ? profilePicture : images.defaultRounded,
}}
style={[
styles.logo,
styles.profileImage,
{ height: height * 0.19 },
]}
resizeMode={Platform.OS === "android" ? "contain" : "cover"}
/>
</View>
{error && (
<Text
style={{
color: COLORS.red,
alignSelf: "stretch",
fontSize: SIZES.body5,
padding: SIZES.padding - 22,
marginTop: 15,
marginHorizontal: SIZES.padding * 3,
}}
>
{error.message || "❌ Oops, something went wrong!"}
</Text>
)}
</>
)}
/>
);
};
export default CustomImagePicker;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
touchPicture: {
zIndex: 10,
marginBottom: -50,
marginLeft: 100,
resizeMode: "contain",
},
logo: {
width: Platform.OS == "android" ? 155 : 164,
maxWidth: 300,
maxHeight: 200,
},
profileImage: {
marginTop: SIZES.padding * 2,
borderRadius: 100,
},
});

Multiple drag and drop simultaneous in React Native

Hello i'm creating a game in react native and i'm stuck because i wan't both players can drag and drop horizontaly an element in same time on the same phone.
I have two components like that:
export class Player1 extends Component{
constructor(props){
super(props);
this.state = {
pan : new Animated.ValueXY()
};
}
componentWillMount(){
this.panResponder = PanResponder.create({
onMoveShouldSetResponderCapture : () => true,
onMoveShouldSetPanResponderCapture : () => true,
onPanResponderGrant : (e, gestureState) => {
this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
this.state.pan.setValue({x: 0, y: 0});
},
onPanResponderMove : Animated.event([null,{
dx : this.state.pan.x,
}]),
onPanResponderRelease: (e, {vx, vy}) => {
}
});
}
render(){
return (
<View style={styles.mainContainer}>
{this.renderDraggable()}
</View>
);
}
renderDraggable(){
return (
<View style={styles.draggableContainer}>
<Animated.View
style={[this.state.pan.getLayout(), styles.triangle]}
{...this.panResponder.panHandlers} >
</Animated.View>
</View>
);
}
}
And in my screen i call my components like that:
export default function HomeScreen() {
return (
<View>
<Player1></Player1>
<Player2></Player2>
</View>
);
}
Thanks for your help
I found a solution, i used react-native-gesture-handle like in the directory doubleDraggable of the example: https://kmagiera.github.io/react-native-gesture-handler/docs/example.html
My Code:
import React, { Component } from 'react';
import { Animated, StyleSheet, View } from 'react-native';
import {
PanGestureHandler,
ScrollView,
State,
} from 'react-native-gesture-handler';
export class Players extends Component {
constructor(props) {
super(props);
this._translateX = new Animated.Value(0);
this._translateY = new Animated.Value(0);
this._lastOffset = { x: 0, y: 0 };
this._onGestureEvent = Animated.event(
[
{
nativeEvent: {
translationX: this._translateX,
},
},
],
);
}
_onHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastOffset.x += event.nativeEvent.translationX;
this._translateX.setOffset(this._lastOffset.x);
this._translateX.setValue(0);
this._translateY.setOffset(this._lastOffset.y);
this._translateY.setValue(0);
}
};
render() {
return (
<PanGestureHandler
{...this.props}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}>
<Animated.View
style={[
styles.box,
{
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
],
},
this.props.boxStyle,
]}
/>
</PanGestureHandler>
);
}
}
export default class Example extends Component {
render() {
return (
<View style={styles.scrollView}>
<DraggableBox />
</View>
);
}
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
box: {
position: 'absolute',
width: 0,
height: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 25,
borderRightWidth: 25,
borderBottomWidth: 50,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
},
});
And Screen:
<View styles={styles.container}>
<Players boxStyle={styles.player1}></Players>
<Players boxStyle={styles.player2}></Players>
</View>
I have been searching for something similar endlessly for a few days but I couldn't find these demos that react-native-gesture-handler provides. Thanks a lot for posting this here #Lillian Pacaud. Here is the link for several of their demos including the draggable component: https://snack.expo.dev/#adamgrzybowski/react-native-gesture-handler-demo
If you need any simultaneous presses/gesture/drags/etc... your best bet is to use react-native-gesture-handler because the native implementation of all touch/gesture-based components in react native don't allow for simultaneous interactions with each especially for Android.
I made a functional component that does the same thing as the accepted answer. Just pass whatever you want to be draggable as a child under the component. It can handle simultaneous drags as well like the accepted answer on both iOS and Android.
Example of using the draggable component:
import React from 'react';
import { View } from 'react-native';
import { DraggableTest } from '../components/test';
export default function Screen() {
return (
<View style={{ flex: 1 }}>
<DraggableTest>
<View
style={{ width: 150, height: 150, backgroundColor: 'lime' }}
/>
</DraggableTest>
</View>
);
}
The draggable component:
import React, { useRef } from 'react';
import { Animated, StyleSheet } from 'react-native';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
export function DraggableTest({ children }) {
const pan = useRef(new Animated.ValueXY()).current;
const lastOffset = useRef({ x: 0, y: 0 }).current;
const onGestureEvent = Animated.event(
[{ nativeEvent: { translationX: pan.x, translationY: pan.y } }],
{ useNativeDriver: false },
);
const onHandlerStateChange = event => {
if (event.nativeEvent.oldState == State.ACTIVE) {
lastOffset.x += event.nativeEvent.translationX;
lastOffset.y += event.nativeEvent.translationY;
pan.setOffset({ x: lastOffset.x, y: lastOffset.y });
pan.setValue({ x: 0, y: 0 });
}
};
return (
<PanGestureHandler
onGestureEvent={onGestureEvent}
onHandlerStateChange={onHandlerStateChange}>
<Animated.View style={[pan.getLayout(), styles.animatedView]}>
{children}
</Animated.View>
</PanGestureHandler>
);
}
const styles = StyleSheet.create({
animatedView: {
position: 'absolute',
},
});

React Native - Touchable Opacity inside Animated.View is firing event of background list view

I Have a list with scroll view, I am trying to add filter options for it. When click on a filter icon, an overlay with position:absolute will be displayed inside a Animated.View. I have Buttons inside overlay View with TouchableOpacity
Filter.js
export default class FilterFade extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
};
componentWillMount() {
this._visibility = new Animated.Value(this.props.visible ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
if (nextProps.visible) {
this.setState({ visible: true });
}
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
}).start(() => {
this.setState({ visible: nextProps.visible });
});
}
render() {
const { visible, style, children, ...rest } = this.props;
const containerStyle = {
opacity: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
scale: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [1.1, 1],
}),
},
],
};
const combinedStyle = [containerStyle, style];
return (
<Animated.View style={combinedStyle} {...rest}>
{children}
</Animated.View>
);
}
}
View.js
<FilterFade visible={this.state.isFilterVisible}>
<View style={styles.filterView}>
<TouchableOpacity onPress={() => this.getFilteedStories}>
<Text style={styles.filterOption}> My Stories </Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.getFilteedStories}>
<Text style={styles.filterOption}> All Stories </Text>
</TouchableOpacity>
</View>
</FilterFade>
Styles
filterView :{
position: 'absolute',
top: 0,
right: 5,
backgroundColor: #CCC,
width: 150,
paddingTop: 15,
paddingBottom: 15,
zIndex: 999,
},
filterOption: {
color: "#FFF",
fontSize: 15
}
Now, When I click on TouchableOpacity Text in Filter, the click event is triggered in Listview which is behind the FadeView.
Can Some one please let me know on how to add press event inside a Animated absolute view.
Thanks in Advance.
Use TouchableOpacity from 'react-native-gesture-handler' instead of from 'react-native'.
import { TouchableOpacity } from 'react-native-gesture-handler';
Follow this post Cannot click TouchableOpacity in animated.view using React native

ListView doesn't scroll

I have a horizontal ListView (shown in the image with list items a,b,c) that will not scroll, or rather, it does scroll - but one out of 10 or so swipes seems to make it scroll. I have also used a FlatList - same result. The ListView is on a Interactable.View, however I got rid of the Interactable.View and it still didn't scroll. I've tested on a real ios device and a genymotion emulator and both had the same result. There are more items in the list than just a,b,c. There are 6 items in the list.
import { StyleSheet, View, Text, FlatList, ListView } from 'react-native'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import {
Button,
Container
} from 'native-base'
import { updateRegion } from './map.action'
import { OptimizedFlatList } from 'react-native-optimized-flatlist'
import Icon from 'react-native-vector-icons/FontAwesome'
import { toggleMenu } from '../search-page/searchPage.action'
import mapStyle from './style'
import Interactable from 'react-native-interactable'
import { setSelectedShop } from '../search-results/searchResults.action'
import { updateHeight } from '../search-results/searchResultsPresenter.action'
import { getSelectedProduct } from './markers.selector'
const mapStateToProps = (state) => ({
region: state.get('map').get('region'),
markers: state.get('searchResults').get('products'),
selectedProduct: getSelectedProduct(state),
height: state.get('searchResultsPresenter').get('height')
})
const mapDispatchToProps = (dispatch) => ({
onRegionChange: (region) => {
dispatch(updateRegion(region))
},
onToggleMenuClick: () => {
dispatch(toggleMenu())
},
setSelectedShop: id => {
dispatch(setSelectedShop(id))
},
updateHeight: height => {
dispatch(updateHeight(height))
}
})
class Map extends Component {
componentDidMount() {
const { store } = this.context
this.unsubscribe = store.subscribe(() => { })
}
componentWillUnmount() {
this.unsubscribe()
}
componentWillReceiveProps(newProps) {
if (newProps.selectedProduct) {
let products = newProps.selectedProduct.products
this.setState({
dataSource: this.state.dataSource.cloneWithRows(products)
})
}
}
interactableView;
constructor(props) {
super(props)
this.state = { dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }) }
}
render() {
console.log(this.props.height)
return (
<Container>
<MapView
style={styles.map}
region={this.props.region}
onRegionChangeComplete={this.props.onRegionChange}>
{
this.props.markers.map(marker => {
return (
<MapView.Marker
coordinate={marker.shop.coordinate}
title={marker.shop.name}
identifier={marker.shop.id.toString()}
onPress={e => {
console.log(e.nativeEvent)
this.interactableView.snapTo({ index: 0 })
this.props.setSelectedShop(marker.shop)
console.log(this.props.selectedProduct)
}}
/>
)
})
}
</MapView>
<Button
small
icon
style={mapStyle.toggleMenuButton}
onPress={() => this.props.onToggleMenuClick()}>
<Icon name="sliders" size={20} color="#FFFFFF" />
</Button>
<Interactable.View
style={{
flex: 1,
flexDirection: 'row',
zIndex: 20,
borderRadius: 10,
backgroundColor: '#222222',
padding: 30,
paddingTop: 50
}}
verticalOnly={true}
snapPoints={[{ y: this.props.height - 225 }, { y: this.props.height - 50 }]}
initialPosition={{ y: this.props.height - 50 }}
ref={view => this.interactableView = view}
onLayout={(event) => {
this.props.updateHeight(event.nativeEvent.layout.height)
}} >
<View style={{ flex: 1, flexDirection: 'row', height: 50 }}>
<Text
style={{
color: 'white',
position: 'absolute',
top: -40,
marginBottom: 20,
textAlign: 'center',
width: '100%'
}}>
{this.props.selectedProduct ? this.props.selectedProduct.shop.name : ''}
</Text>
<ListView
dataSource={this.state.dataSource}
horizontal={true}
style={{
height: 200
}}
renderRow={(rowData)=> {
console.log(rowData)
return (
<View style={{
backgroundColor: 'blue',
width: 100,
borderWidth: 1,
borderColor: 'black',
margin: 0
}}>
<Text style={{ color: 'white' }}>{rowData.name}</Text>
<View style={{
zIndex: 15,
width: '100%',
height: '100%',
backgroundColor: 'red'
}}>
</View>
</View>
)
}}
/>
</View>
</Interactable.View>
</Container>
)
}
}
Map.contextTypes = {
store: React.PropTypes.object
}
Map.propTypes = {
region: React.PropTypes.shape({
latitude: React.PropTypes.number,
longitude: React.PropTypes.number,
latitudeDelta: React.PropTypes.number,
longitudeDelta: React.PropTypes.number
}).isRequired,
height: React.PropTypes.number,
updateHeight: React.PropTypes.func,
setSelectedShop: React.PropTypes.func,
selectedProduct: React.PropTypes.string,
onRegionChange: React.PropTypes.func.isRequired,
onToggleMenuClick: React.PropTypes.func.isRequired,
markers: React.PropTypes.array
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Map)
const styles = StyleSheet.create({
map: {
...StyleSheet.absoluteFillObject,
zIndex: 3
}
})
How do I get the listview to scroll with every swipe, rather than about 1 out of 10 swipes?
Same thing happens when I use a ScrollView. So nothing can be swiped horizontally there. Even when removing the interactable.view
Making renderRow return a TouchableHighlight instead of a View caused the list to be draggable with every touch and swipe. And finally, unessential to the answer, I switched from a ListView to a FlatList because ListView is going to be deprecated in the future. I felt it good to state that as at this point in time it isn't clear from the official documentation that ListView is becoming deprecated.