Know which element was touched with React Native's PanResponder? - react-native

Im making a drag and drop with the PanResponder but I only want part of a component to be the 'target' which initiates the drag. Many elements are contained within a ScrollView, and dragging on the elements outside of the 'target' should scroll as usual.
const Dragger = () => {
return (
<View {...panResponder.panHandlers}>
<View style={{ flexDirection: 'row' }}>
<Text>Drag me</Text>
<Text>Im not draggable</Text>
</View>
<View style={{ flexDirection: 'row' }}>
<Text>Drag me</Text>
<Text>Im not draggable</Text>
</View>
</View>
)
}
const Parent = () => {
return(
<ScrollView>
<Text>Stuff</Text>
<Dragger />
<Text>Stuff</Text>
<ScrollView/>
)
}
Can this be done or do I need to attach multiple panHandlers to each <Text>Drag me</Text>?
From the docs I see there is a nativeEvent.target which returns "The node id of the element receiving the touch event", however it's just a number so Im not sure how to use it?
Would I need to find out the node IDs of the <Text>Drag me</Text> elements and see if the number that came back is in that list?
UPDATE: Here is a more visual demo of what Im trying to do:
https://snack.expo.io/#jamesweblondon/rude-pretzel
I need PanResponder drag events on the grey boxes, but not on the text inputs:
import React from "react";
import { StyleSheet, Text, View, TextInput } from "react-native";
const items = new Array(50).fill(0).map((item, index) => {
return {
id: index,
backgroundColor: `rgb(${Math.floor(Math.random() * 255)}, ${
index * 5
}, ${132})`,
};
});
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
item: {
display: "flex",
flexDirection: "row",
height: 200,
width: "100%",
justifyContent: "space-around",
alignItems: "center",
},
input: {
color: "white",
fontSize: 20,
height: "100%",
flex: 1,
},
handle: {
display: "flex",
backgroundColor: "grey",
height: "80%",
width: 100,
marginHorizontal: 20,
},
});
export default function App() {
return (
<View style={styles.container}>
{items.map((item) => {
return (
<View
style={[styles.item, { backgroundColor: item.backgroundColor }]}
>
<View style={styles.handle} />
<TextInput style={styles.input} defaultValue={item.id} />
</View>
);
})}
<Text>Open up App.js to start working on your app!</Text>
</View>
);
}

You started your question with some snippets that I couldn't find in the link you posted, so I started from that and created a fork.
So in order to get the PanResponder drag events on the grey boxes, but not on the text inputs you should do like this on your item component:
<View style={[styles.item, { backgroundColor: item.backgroundColor }]} >
<View style={styles.handle} {...panResponder.panHandlers} />
<TextInput style={styles.input}>{item.id}</TextInput>
</View>
For the PanResponder i used the default config from https://reactnative.dev/docs/panresponder#example
Here's an update on your code to try it.
Hope this helps! Tell me if this isn't working for you.

Related

How to achieve the desired UI in react native

I am trying to acheive a styling as shown in the image attached below
See how here the fingerprint icon happens to be inside the border of the Textinput field but instead I am getting the output as shown below
PS:- ignore the left and right side black color borders it happens to be the simulator body just focus on the UI.
Here's my code :-
import { View, Text, TextInput, StyleSheet } from 'react-native'
import React from 'react'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
const TestAtom = () => {
return (
<View style={styles.searchSection}>
<TextInput style={styles.input} placeholder='User' onChangeText={(searchString) => {this.setState({searchString})}}
underlineColorAndroid="transparent"/>
<Icon style={styles.searchIcon} name='fingerprint' size={20} color= '#000' />
</View>
)
}
const styles = StyleSheet.create({
searchSection: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
searchIcon: {
// padding: 10
paddingRight: 10
},
input: {
flex: 1,
paddingTop: 10,
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 0,
backgroundColor: '#fff',
color: '#424242',
borderBottomColor: '#000',
borderBottomWidth: 1
}
});
export default TestAtom
Can anyone help me with it.
You should make a View and place the Text Input and Fingerprint icon inside.
A small example of how it will look like.
<View style={{
borderWidth:1,
flex:1,
flexDirection:'row',
alignItems:'center'
}}>
<TextInput/>
<FingerIcon/>
</View>
Have the Textinput and the fingerprint icon as two components in the same view styled as flex-direction:row. Have the bottom ruler part as a separate item which draws under the View of Textinput and the Icon.
import { View, StyleSheet,SafeAreaView,TextInput } from 'react-native'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
function UIComponent(){
<View style={styles.container}>
<View style={styles.componentWrapper}>
<View style={styles.passwordFieldWrapper}>
<TextInput style={styles.textInput} placeholder='umana Password'>
</TextInput>
<MaterialCommunityIcons name="fingerprint" color='green' size={24} />
</View>
<View style={styles.bottomPart}>
</View>
</View>
</View>
}
export default UIComponent
const styles = StyleSheet.create({
container:{
backgroundColor:'#ffffff',
flex:1
},
componentWrapper:{
alignItems:'center',
justifyContent:'center'
},
passwordFieldWrapper:{
flexDirection:'row'
},
textInput:{
width:'50%'
},
bottomPart:{
marginTop:3,
borderBottomColor:'gray',
borderBottomWidth:1,
width:'60%'
}
})

How to make TouchableOpacity image look like pressed in?

I have an image in each TouchableOpacity and I would like to change the picture in every onPress function so she could looked like shes pressed in (for example: remove the color from to picture and changes it to black and white or make a light gray shadow on the picture ).
and Reverse (when you click shes changing back to the original picture (Press:true/false).
I have a stateless Component and no class.
My Component :
export default function Recipie({ navigation, route }) {
const recipies = GetRecipies();
return (
<View style={{ flexGrow: 1, flex: 1 }}>
<ScrollView>
{recipies.map((u, i) => {
return (
<View key={i}>
<Text
onPress={navigation.navigate}
style={{
fontSize: 25,
fontFamily: "Cochin",
textAlign: "center",
}}
>
{u._recipieName}
</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate("SingleRecipieScreen", { u });
}}
>
<Image
style={{
height: 200,
width: 350,
borderRadius: 80,
alignSelf: "center",
}}
source={{ uri: u._imgUrl }}
/>
</TouchableOpacity>
<Text
style={{
fontSize: 17,
fontFamily: "Cochin",
textAlign: "center",
}}
>
{u._recipieDescription}
</Text>
<TouchableOpacity
style={{ flex: 1, flexDirection: "column", flexGrow: 1 }}
>
{Show(u._preparationTime)}
</TouchableOpacity>
</View>
);
})}
</ScrollView>
</View>
);
}
Try to use position absolute in View to cover button , and useState for styles, example :
import React, { useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View,Image } from "react-native";
const App = () => {
const [isPressed, setIsPressed] = useState(0);
const onPress = () => setIsPressed(!isPressed);
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={onPress}
>
<View style={isPressed && styles.pressedButtonStyle} />
<Text> {isPressed ? "Pressed" : " Press Here"}</Text>
<Image
style={ styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingHorizontal: 10,
},
button: {
alignItems: "center",
backgroundColor: "#DDDDDD",
},
tinyLogo: {
width: 50,
height: 50,
},
pressedButtonStyle: {
position:"absolute",
width:"100%",
height:"100%",
backgroundColor:'black',
opacity:0.6,
zIndex:100,
}
});
https://snack.expo.dev/ixeOwAg3o

Center Text in FlatList next to Icon - React Native

I am currently trying to center the text from the FlatList.
By centering I mean I want it to be in the middle, right of each Icon.
Currently this is how is displaying, it's on the bottom. I just want it to be a little bit higher:
Here is how my component looks like:
I have tried to implement few styles, but still no success.
The thing that crossed my mind but did not try was to hard code each line and drop the loop, but I am not sure if this is right to do.
import SocialIcon from 'react-native-vector-icons/AntDesign';
import BookingIcon from 'react-native-vector-icons/FontAwesome';
import FeatherIcons from 'react-native-vector-icons/Feather';
export const bookIcon = (<BookingIcon name="pencil-square-o" size={40} color="purple" />);
export const calendarIcon = (<SocialIcon name="calendar" size={40} color="purple" />);
export const questionIcon = (<SocialIcon name="questioncircleo" size={40} color="purple" />);
export const externalLinkIcon = (<FeatherIcons name="external-link" size={40} color="purple" />);
class AboutMe extends Component {
static navigationOptions = {
title: "About Me",
}
render() {
return (
<View style={styles.container}>
<View style={styles.topBox}>
<View style={styles.circleOuter} />
<View style={styles.circle} />
</View>
<View style={styles.middleBox}>
</View>
<View style={styles.bottomBox}>
<FlatList
contentContainerStyle={styles.listItem}
data={[
{key: 'Book a free appointment', page:'Book', icon: bookIcon},
{key: 'Availability', page:'Availability', icon:calendarIcon},
{key: 'FAQ', page:'Faq', icon: questionIcon},
{key: 'Useful Links', page: 'Links', icon: externalLinkIcon},
]}
onPress={() => this.props.navigation.navigate('Book')}
renderItem={({item}) => {
return (
<TouchableHighlight onPress={() => this.props.navigation.navigate(`${item.page}`)}>
<Text >{item.icon}{item.key}</Text>
</TouchableHighlight>
)
}}
/>
</View>
</View>
);
};
};
const styles = StyleSheet.create({
container: {
flex: 1
},
topBox: {
flex:3,
backgroundColor: 'red',
justifyContent:'center',
alignItems: 'center'
},
middleBox: {
flex:1,
backgroundColor: 'yellow'
},
bottomBox: {
flex:4,
backgroundColor: 'orange',
padding: 20
},
circle: {
width: 160,
height: 160,
borderRadius: 160/2,
backgroundColor: 'green',
position: 'absolute'
},
circleOuter: {
width: 180,
height: 180,
borderRadius: 180/2,
backgroundColor: 'black'
},
listItem: {
flex:1,
justifyContent: 'center'
},
text: {
fontSize: 20,
}
});
You need to wrap the Text tag in a View/TouchableHighlight tag and then center the text tag vertically. Try this and let me know. It could be necessary that you wrap the icons in a separated image tag. if the code avode dont work means that a separeted tag is necessary so let me know!
<TouchableHighlight
style={styles.buttonsStyle}
onPress={() => this.props.navigation.navigate(`${item.page}`)}
>
<Text>{item.icon}{item.key}</Text>
</TouchableHighlight>
...
...
...
//in the styles add
buttonsStyle:{
justifyContent: 'center',
}
EDIT1
In order to wrap the icons it should be like following.. Note that you cannot use TouchableHighlight in this case. it seems to be a bug with react-native. aslo i used TouchableOpacity
renderItem={({item}) => {
return (
<TouchableOpacity
style={styles.buttonsStyle}
onPress={() => this.props.navigation.navigate(`${item.page}`)}
>
<Image style={styles.imgStyles} source={item.icon} />
<Text style={styles.mappedTextStyle}>{item.key}</Text>
</TouchableOpacity>
)
}}
styles to change/add
buttonsStyle:{
alignItems: 'center',
flexDirection:'row',
marginTop: 5,
},
imgStyles:{
width: 40,
height: 40,
marginRight: 10
},
mappedTextStyle: {
fontSize: 18,
color: 'black'
},
EDIT 2
In order to cover vector-icons lib, i have created a Expo snack for producing the desired behavior. Expo snack has also the expected solution for the problem Expo Snack

Align a component to right in react native

In my application, I want to align a component (searchBar) to the right corner of my screen.
The mainWrapper has flexDirection RIGHT, so I tried to use justifyContent: "flex-end". But it did not work. What am I doing wrong here?
import React from "react";
import { View, Text, StyleSheet, Dimensions } from "react-native";
import { DefaultButton , ClickableIcon} from "../../mixing/UI";
import { Search } from "../../config/images";
const width = Dimensions.get("window").width;
const chooseWorkoutHeader = props => (
<View style={styles.mainWrapper}>
<View style={styles.buttonWrapper}>
<DefaultButton
buttonText={styles.buttonText}
width={width * 0.3}
backgroundColor="white"
onPress={() => props.onClickPlaylists}>
Playlists
</DefaultButton>
<DefaultButton
buttonText={styles.buttonText}
width={width * 0.3}
backgroundColor="white"
onPress={() => props.onClickSortBy}>
Sort By
</DefaultButton>
</View>
{/* <View style={styles.searchBar}> */}
<ClickableIcon
source={Search}
height={35}
width={35}
style={styles.searchBar}
// onIconPressed={() => {
// navigation.navigate("mainfeed");
// }}
/>
{/* </View> */}
</View>
);
const styles = StyleSheet.create({
mainWrapper: {
borderWidth: 3,
flexDirection: "row",
padding: 10,
width: width,
backgroundColor: "white"
},
buttonWrapper: {
flexDirection: "row",
justifyContent: "space-between",
width: width * 0.62
},
buttonText: {
fontSize: 18,
textAlign: "center",
color: "gray"
},
searchBar: {
borderWidth: 1,
padding: 1,
justifyContent: 'flex-end'
}
});
export default chooseWorkoutHeader;
I tried alignSelf as well. But none of them worked. How to move the ClickableIcon which has styles as "searchBar" to the right of the screen?
You can achive this by putting flex: 1 for buttonWrapper
buttonWrapper: {
flexDirection: "row",
justifyContent: "space-between",
width: width * 0.62,
flex: 1, // Add this line
}
You have two component inside mainWrapper (buttonWrapper and ClickableIcon). flex: 1 will expand maximum with of buttonWrapper and leave space for ClickableIcon in right side.
Do you tried adding this to your "mainWrapper":
display: "flex"
As far as i know "flex" only workings when you also set the display attribute

React Native can't click TouchableOpacity or TextInput

In my React Native project I have a TextInput and a TouchableOpacity component at the top of the screen that for some reason cannot be clicked. It worked at some point while I was working on the project, but for some reason it no longer recognizes clicks or key events. The component is as follows:
import React, { Component } from "react";
import {
AsyncStorage,
Dimensions,
FlatList,
Platform,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View
} from "react-native";
import styles from "./style/styles";
import NotificationBar from "./components/NotificationBar";
export default class App extends Component {
constructor(props) {
super(props);
this.data = [];
this._initData();
}
_initData() {
for (let i = 0; i < 500; i++) {
this.data.push({
// key: i,
id: "ABC" + i,
name: "Random Name",
value: 50 * i
});
}
}
_renderItem = ({ item }) => {
return (
<View style={styles.itemContainer}>
<Text style={styles.textItem}>{item.id}</Text>
</View>
);
};
render() {
return (
<View>
<NotificationBar />
<View style={{ flex: 1, flexDirection: "column" }}>
<View style={{ flex: 1, flexDirection: "row" }}>
<View
style={{
width: (Dimensions.get("window").width / 3) * 2,
height: 50,
backgroundColor: "white"
}}
>
<TextInput
placeholder="New List Name"
style={styles.textInputStyle}
/>
</View>
<View
style={{
width: Dimensions.get("window").width / 3,
height: 50,
backgroundColor: "#4ce31e"
}}
>
<TouchableOpacity
style={styles.button}
onPress={this.testSetStorage}
>
<Text> Create List </Text>
</TouchableOpacity>
</View>
</View>
</View>
<View style={{ paddingTop: 50 }}>
<FlatList
data={this.data}
renderItem={this._renderItem}
style={{ alignSelf: "stretch" }}
keyExtractor={(item, index) => item.id}
/>
</View>
</View>
);
}
}
The style sheet is:
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
statusBarBackground: {
height: 20,
backgroundColor: 'white',
},
textInputStyle: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
textAlign: 'center',
borderWidth: 1,
borderRadius: 6,
},
button: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
width: 100,
paddingLeft: 20,
},
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 10
},
});
My best guess is that it the issue would have something to do with the parent View components that the TouchableOpacity and TextInput components are in, but I'm not sure. How can I get those components to recognize clicks and respond to them? I'm running on an iPhone 7 emulator.
You have not defined testSetStorage function in your component.
You need to testSetStorage = () => {console.log("Tapped")} outside render function.