How can I implement #mention in react native's TextInput?
I've tried this react-native-mention but it is not being maintained anymore. There are so many styling issues and callback issues.
What I want is to display custom view inside TextInput. Something like this.
And after tapping on the list I want to display like this:
So far I am able to achieve:
When I type '#' in TextInput user list appear.
And when I tap on user I get username in TextInput
Code:
renderSuggestionsRow() {
return this.props.stackUsers.map((item, index) => {
return (
<TouchableOpacity key={`index-${index}`} onPress={() => this.onSuggestionTap(item.label)}>
<View style={styles.suggestionsRowContainer}>
<View style={styles.userIconBox}>
<Text style={styles.usernameInitials}>{!!item.label && item.label.substring(0, 2).toUpperCase()}</Text>
</View>
<View style={styles.userDetailsBox}>
<Text style={styles.displayNameText}>{item.label}</Text>
<Text style={styles.usernameText}>#{item.label}</Text>
</View>
</View>
</TouchableOpacity>
)
});
}
onSuggestionTap(username) {
this.setState({
comment: this.state.comment.slice(0, this.state.comment.indexOf('#')) + '#'+username,
active: false
});
}
handleChatText(value) {
if(value.includes('#')) {
if(value.match(/#/g).length > 0) {
this.setState({active: true});
}
} else {
this.setState({active: false});
}
this.setState({comment: value});
}
render() {
const {comments} = this.state;
return (
<View style={styles.container}>
{
this.state.active ?
<View style={{ marginLeft: 20}}>
{this.renderSuggestionsRow()}
</View> : null
}
<View style={{ height: 55}}/>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputChat}
onChangeText={(value) => this.handleChatText(value)}
>
{comment}
</TextInput>
<TouchableOpacity style={styles.inputIcon} onPress={() => this.addComment()}>
<Icon type='FontAwesome' name='send-o' style={{fontSize: 16, color: '#FFF'}}/>
</TouchableOpacity>
</View>
</View>
);
}
One simple solution would be to use react-native-parsed-text.
Here is an example:
import * as React from "react";
import { Text, View, StyleSheet } from 'react-native';
import ParsedText from 'react-native-parsed-text';
const userNameRegEx = new RegExp(/#([\w\d.\-_]+)?/g);
export default class Example extends React.Component {
handleNamePress = (name) => {
alert("Pressed username " + name);
}
render() {
return (
<View style={styles.container}>
<ParsedText
style={styles.text}
parse={
[
{pattern: userNameRegEx, style: styles.username, onPress: this.handleNamePress},
]
}
childrenProps={{allowFontScaling: false}}
>
This is a text with #someone mentioned!
</ParsedText>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
text: {
color: 'black',
fontSize: 15,
},
username: {
color: 'white',
fontWeight: 'bold',
backgroundColor: "purple",
paddingHorizontal: 4,
paddingBottom: 2,
borderRadius: 4,
},
});
However, this library doesn't support rendering custom views. The example above is achieved by just pure styling. If you need a custom view you need to implement something yourself. For a long time, it wasn't possible to render arbitrary components embedded inside a text-components. However, this has changed now afaik and we can do stuff like this:
<Text>Hello I am an example <View style={{ height: 25, width: 25, backgroundColor: "blue"}}></View> with an arbitrary view!</Text>
Check both code examples here: https://snack.expo.io/#hannojg/restless-salsa
One important note: You can render the output of the ParsedText or your own custom component inside the TextInput, like this:
<TextInput
...
>
<ParsedText
...
>
{inputValue}
</ParsedText>
</TextInput>
Related
I'm currently working on a whishlist, I've tried two different methods.
The first one works but if I press my like ( heart icon), all the heart get fill.
The second method I can only select one article, not few.
My hook state first :
const [liked, setLiked] = useState(false)
const [counter, setCounter] =useState(-2)
const[likedProduct, setLikedProduct]=useState(false)
My code for the first point :
if(likedProduct){
var colorLike = {color: 'red'}
} else {
var colorLike = {}
}
var ArticleList = articleData.map((article, i) => {
return (<View style={{width: '42%'}}>
<TouchableOpacity
onPress={() => {
onSubmitProduct(productId)
navigation.navigate('Product')
}
}
>
<Image source={ article.img} style={{ height: 250, width: 200} } />
<View style={{ flex: 1, flexDirection: 'row', marginTop: 5, justifyContent: "space-between" }}>
<Text style={{ fontWeight: 'bold' }}>{article.brand}</Text>
<FontAwesome name="heart" size={20} style={colorLike}
onPress={() => setLikedProduct(!likedProduct)
}
/>
</View>`
The code of my second point :
<AntDesign name={liked && i== counter ? "heart":"hearto"} size={20} color="red"
onPress={()=>{
setLiked(!liked)
setCounter(i)
Any idea to get multiple like ( onPress on multiple heart) ?
Thanks
It's relatively simple to add this functionality to the app, below is the behavior of the final sample app:
import React, { useState } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import { FontAwesome } from '#expo/vector-icons';
const articleData = ['one', 'two', 'three', 'four', 'five'];
export default function App() {
/*
to determine if the list item is liked or not,
we will set up a state "liked", which is an array and holds
the indexes of liked list items.
*/
const [liked, setLiked] = useState([]);
/*
on TouchableOpacity we will use the below function,
it will first see if the index of cliked list item is already present,
if yes then we remove that index from "liked" state array, which is
similar to unlike functionality, and if the index is not present,
then we push that index to the "liked" state.
-----------------------------------------
onPress={() => {
console.log(liked);
if (liked.includes(index)) {
let unlike = liked.filter((elem) => elem !== index);
setLiked(unlike);
} else {
setLiked([...liked, index]);
}
}}
------------------------------------------
then comes the styling of the icon, which is pretty simple,
we just see if the index of that list item is present in the "liked" state,
if yes then it means, that item is liked and we set the color of the icon "red" else "black"
<FontAwesome
name="heart"
size={20}
😏 ➡ style={{ color: liked.includes(index) ? 'red' : 'black' }}
/>
*/
return (
<View style={styles.container}>
{articleData.map((article, index) => (
<TouchableOpacity
onPress={() => {
console.log(liked);
if (liked.includes(index)) {
let unlike = liked.filter((elem) => elem !== index);
setLiked(unlike);
} else {
setLiked([...liked, index]);
}
}}>
<View style={styles.list}>
<Text>{article}</Text>
<FontAwesome
name="heart"
size={20}
style={{ color: liked.includes(index) ? 'red' : 'black' }}
/>
</View>
</TouchableOpacity>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
list: {
padding: 10,
margin: 5,
flexDirection: 'row',
flex: 1,
justifyContent: 'space-between',
backgroundColor: 'white',
},
});
Working App : Expo Snack
We created a FlatList component and a Contact screen in our app. We want to add 'heart' icon to the near of the images in Contact screen. We added heart icon near to all of items. But, if we pressed one of these icons, it changes all of theirs colors to red, not only one of them. We want to change the color of clicked item.
Screenshot of our program:
This is our FlatList component:
import React, { Component } from 'react';
import { View, Text, SafeAreaView, StyleSheet, FlatList, Image, TouchableOpacity,
TouchableWithoutFeedback, TextInput } from 'react-native';
import { Right, Icon } from 'native-base';
import data from '../../data';
export default class FlatListComponent extends Component {
state = {
text: '',
contacts: data,
like: false,
color: 'white',
}
toggleLike=()=>{
this.setState({
like: !this.state.like
})
if(this.state.like){
this.setState({
color: 'red',
})
}else{
this.setState({
color: 'white',
})
}
}
renderContactsItem = ({item, index}) => {
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={this.toggleLike}>
{/*{this.like ? (
<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'red'}} />
) :
( <Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'white'}} /> )
}*/}
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.color === "white" ? 'white' :'red', paddingRight: 10 }}/>
{/*<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: this.state.color}} />*/}
</TouchableWithoutFeedback>
</Right>
</View>
);
}
searchFilter = text => {
const newData = data.filter(item => {
const listItems = `${item.first_name.toLowerCase()}`
return listItems.indexOf(text.toLowerCase()) > -1;
});
this.setState({
contacts: newData,
});
};
renderHeader = () => {
const {text} = this.state;
return (
<View style={styles.searchContainer}>
<TextInput
onChangeText = {text => {
this.setState ({
text,
});
this.searchFilter(text);
}}
value={text}
placeholder="Search..."
style={styles.searchInput} />
</View>
)
}
render() {
return (
<FlatList
ListHeaderComponent={this.renderHeader()}
renderItem={this.renderContactsItem}
keyExtractor={item => item.id}
data={this.state.contacts}
/>
);
}
}
const styles = StyleSheet.create({
itemContainer: {
flex: 1,
flexDirection: 'row',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee'
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
marginHorizontal: 10,
},
textContainer: {
justifyContent: 'space-around',
},
name: {
fontSize: 16,
},
searchContainer: {
padding: 10
},
searchInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
}
});
Our Contact screen is just:
import React from 'react';
import 'SafeAreaView' from 'react-native';
import FlatList from './FlatList';
export default function Contact() {
<SafeAreaView style={{ flex: 1 }}>
<FlatList />
</SafeAreaView>
}
How can we implement this?
I've run into this recently :) One option is to make the renderContactsItem its own component. For example:
const RenderContactsItem = ({item, index}) => {
const [like, setLike] = useState(false);
const [color, setColor] = useState("white");
const toggleLike = () => {
setLike(!like)
if(like) {
setColor("red");
} else {
setColor("white");
}
}
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={toggleLike}>
<Icon name='heart' type='FontAwesome' size={32} style={{color, paddingRight: 10 }}/>
</TouchableWithoutFeedback>
</Right>
</View>
);
}
In this case, each item manages its own state, so setting like doesn't set it for every item.
Another option would be to build an object with "like" states and set the values as the hearts are pressed. For example:
state = {
text: '',
contacts: data,
like: {},
color: 'white', // You don't need this
}
Then, when a heart is pressed, you can send toggleLike the index, and set the state like so:
toggleLike = (index) => {
let newLike = {...this.state.like};
newLike[index] = !Boolean(newLike[index]);
this.setState({
like: newLike,
});
}
And render the color conditionally depending on the index value of the like state, like so:
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.like[index] ? 'red' :'white', paddingRight: 10 }}/>
I have a value in my state that changes based on a Switch. Depending on the value of that state item I want to change the styling of my button. I can see the state change, but the color doesn't change.
This seems straightforward as shown in this documentation: https://reactjs.org/docs/conditional-rendering.html
I actually have one conditional rendering working in the same statement, but it is referencing redux state instead of component state.
I've included the relevant aspects of my code below and tried to strip out the unnecessary stuff.
Still kind of a bit long.
/* eslint-disable prettier/prettier */
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity,
Dimensions,
StyleSheet,
TextInput,
Switch,
ActivityIndicator,
} from 'react-native';
import { connect } from 'react-redux';
// Actions
import { createUser } from '../../actions/user-actions';
// Getting dims
const { width: WIDTH } = Dimensions.get('window');
// Styling
const styles = StyleSheet.create({
containerStyle: {
flex: 1,
backgroundColor: 'black',
alignItems: 'center',
justifyContent: 'center',
},
footerContainerStyle: {
justifyContent: 'flex-start',
flex: 3,
width: WIDTH,
paddingHorizontal: 30,
alignItems: 'center',
marginTop: 45,
},
tcStyle: {
flexDirection: 'row',
marginBottom: 20,
},
switchStyle: {
marginRight: 15,
},
buttonStyle: {
width: WIDTH-100,
height: 50,
backgroundColor: '#007AFF',
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
},
disabledButtonStyle: {
width: WIDTH-100,
height: 50,
backgroundColor: '#007AFF',
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 20,
opacity: 0.3,
},
buttonTextStyle: {
color: 'white',
fontSize: 14,
fontWeight: 'bold',
},
linkStyle: {
textDecorationLine: 'underline',
color: 'blue',
},
});
// Component
class Signup extends Component {
constructor(props) {
super(props);
this.state = {
switchValue: false,
email: '',
name: '',
password: '',
passwordConfirm: '',
errorMessage: '',
};
}
componentDidUpdate() {
console.log(this.props.users);
const { navigation } = this.props;
if (this.props.users.user) {
navigation.navigate('Home');
}
}
componentDidMount() {
console.log(this.props);
}
// Helper functions
toggleSwitch = (value) => {
this.setState({switchValue: value});
};
onSignUp = () => {
const { email, name, password, passwordConfirm, switchValue } = this.state;
const { createUser } = this.props;
if (password !== passwordConfirm) {
console.log('Passwords do not match')
this.setState({errorMessage: 'Passwords do not match'});
return;
}
if (!switchValue) {
console.log('You must agree to terms');
this.setState({errorMessage: 'You must agree to terms'});
return;
}
createUser(email, password, name);
}
render() {
// Conditional button rendering
const { email, password, passwordConfirm, name, switchValue } = this.state;
let button;
console.log(switchValue);
if (this.props.users.loading) {
button = (
<TouchableOpacity style={[styles.buttonStyle]} onPress={this.onSignUp}>
<ActivityIndicator size="large" />
</TouchableOpacity>
);
} else if (switchValue) {
button = (
<TouchableOpacity style={[styles.buttonStyle]} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT</Text>
</TouchableOpacity>
)
} else if (!switchValue) {
button = (
<TouchableOpacity style={[styles.disabledButtonStyle]} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT</Text>
</TouchableOpacity>
)
}
return (
<View style={styles.containerStyle}>
<View style={styles.footerContainerStyle}>
<View style={styles.tcStyle}>
<Switch
onValueChange = {this.toggleSwitch}
value = {this.state.switchValue}
style = {styles.switchStyle}
trackColor={{true: '#007AFF', false: 'grey'}}
/>
<Text style={{color: 'white', flexWrap: 'wrap', flex: 1}}>I have read & agree to the <Text style={styles.linkStyle}>Terms of Use</Text> and <Text style={styles.linkStyle}>Privacy Policy</Text></Text>
</View>
{button}
<View style={styles.textLinkStyle}>
<Text style={styles.ctaHelpTextStyle}>Have an account?</Text>
<TouchableOpacity>
<Text style={styles.ctaTextStyle}> Sign In</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
// state mapping
const mapStateToProps = ({ users }) => ({
users,
});
// Export
export default connect(mapStateToProps, {
createUser,
})(Signup);
If I toggle that switch I am expecting the button component to change to the one with the other styling. That's not happening though.
Actually your state changes correctly, but opacity does not work.
Update:
it seems react native has issues changing TouchableOpacity's opacity.
One solution is removing the opacity from the styles. And wrapping TouchableOpacity component with a View component, giving View an opacity.
You can try like this:
else if (switchValue) {
button = (
<View opacity={0.5}>
<TouchableOpacity style={styles.buttonStyle} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT true</Text>
</TouchableOpacity>
</View>
);
} else if (!switchValue) {
button = (
<View opacity={0.1}>
<TouchableOpacity
style={styles.disabledButtonStyle}
onPress={this.onSignUp}
>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT false</Text>
</TouchableOpacity>
</View>
);
add return in conditions for example
if (this.props.users.loading) {
return(
<TouchableOpacity style={[styles.buttonStyle]} onPress{this.onSignUp}>
<ActivityIndicator size="large" />
</TouchableOpacity>
);
}
Based on you code, the only difference between both buttons is just the styling.
The way i would approach this is simply make the conditioning inside the styling its self not an if statement on the render method itself, follow along:
Solution
Modify this fragment:
// ... Other code parts
if (this.props.users.loading) {
button = (
<TouchableOpacity style={[styles.buttonStyle]} onPress={this.onSignUp}>
<ActivityIndicator size="large" />
</TouchableOpacity>
);
} else if (switchValue) {
button = (
<TouchableOpacity style={[styles.buttonStyle]} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT</Text>
</TouchableOpacity>
)
} else if (!switchValue) {
button = (
<TouchableOpacity style={[styles.disabledButtonStyle]} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT</Text>
</TouchableOpacity>
)
}
to this:
// ... Other code parts
if (this.props.users.loading) {
button = (
<TouchableOpacity style={[styles.buttonStyle]} onPress={this.onSignUp}>
<ActivityIndicator size="large" />
</TouchableOpacity>
);
} else {
button = (
<TouchableOpacity style={switchValue ? styles.buttonStyle : styles.disabledButtonStyle} onPress={this.onSignUp}>
<Text style={styles.buttonTextStyle}>CREATE AN ACCOUNT</Text>
</TouchableOpacity>
)
}
Hope this Helps!
I have just started using React Native with Expo so I am kind of confused. So, I have made a camera component which I imported in the main screen. Everything looks good. But I can't take pictures. I cannot click the snap icon and save the image. Is there a component that I missed?
I have only posted the CameraComponent class below.
Camera.js
class CameraComponent extends Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' })
}
render() {
const { hasCameraPermission } = this.state
if (hasCameraPermission === null) {
return <View />
}
else if (hasCameraPermission === false) {
return <Text> No access to camera</Text>
}
else {
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1, justifyContent: 'space-between' }}
type={this.state.type}
>
<Header
searchBar
rounded
style={{
position: 'absolute',
backgroundColor: 'transparent',
left: 0,
top: 0,
right: 0,
zIndex: 100,
alignItems: 'center'
}}
>
<View style={{ flexDirection: 'row', flex: 4 }}>
<Ionicons name="md-camera" style={{ color: 'white' }} />
<Item style={{ backgroundColor: 'transparent' }}>
<Icon name="ios-search" style={{ color: 'white', fontSize: 24, fontWeight: 'bold' }}></Icon>
</Item>
</View>
<View style={{ flexDirection: 'row', flex: 2, justifyContent: 'space-around' }}>
<Icon name="ios-flash" style={{ color: 'white', fontWeight: 'bold' }} />
<Icon
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back ?
Camera.Constants.Type.front :
Camera.Constants.Type.back
})
}}
name="ios-reverse-camera"
style={{ color: 'white', fontWeight: 'bold' }}
/>
</View>
</Header>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 30, marginBottom: 15, alignItems: 'flex-end' }}>
<Ionicons name="ios-map" style={{ color: 'white', fontSize: 36 }}></Ionicons>
<View></View>
<View style={{ alignItems: 'center' }}>
<MaterialCommunityIcons name="circle-outline" // This is the icon which should take and save image
style={{ color: 'white', fontSize: 100 }}
></MaterialCommunityIcons>
<Icon name="ios-images" style={{ color: 'white', fontSize: 36 }} />
</View>
</View>
</Camera>
</View>
)
}
}
}
export default CameraComponent;
The icon in the center i;e circle icon should automatically take and save image.
You can use "onPictureSaved" when the asynchronous takePictureAsync function returns so that you can grab the photo object:
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = photo => {
console.log(photo);
}
In the view you would have a Camera component that has a ref:
<Camera style={styles.camera} type={this.state.type} ref={(ref) => { this.camera = ref }} >
As well as a button that will call the takePicture function on press:
<TouchableOpacity style={styles.captureButton} onPress={this.takePicture} />
So you need to tell your 'circle icon' to take the picture. First I would add a reference to your camera like so
<Camera style={{ flex: 1 }}
ref={ (ref) => {this.camera = ref} }
type={this.state.type}>
then create a function that actually tells your app to take the photo:
async snapPhoto() {
console.log('Button Pressed');
if (this.camera) {
console.log('Taking photo');
const options = { quality: 1, base64: true, fixOrientation: true,
exif: true};
await this.camera.takePictureAsync(options).then(photo => {
photo.exif.Orientation = 1;
console.log(photo);
});
}
}
Now make your icon have an onPress() to take the photo. I did something like this.
<TouchableOpacity style={styles.captureButton} onPress={this.snapPhoto.bind(this)}>
<Image style={{width: 100, height: 100}} source={require('../assets/capture.png')}
/>
</TouchableOpacity>
You may also want to create a view that renders an image preview or something similar. The Expo documentation has a fairly good example on getting started. Note that Expo creates a cached folder called 'Camera' and that's where the image initially is.
You can do this in a functional component as well using a ref created via a React Hook. Here is an example based on the expo SDK 38 Camera component https://docs.expo.io/versions/v38.0.0/sdk/camera/
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const ref = useRef(null)
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
_takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={type} ref={ref}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}> Flip </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={_takePhoto}
>
<Text>Snap Photo</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
I did not check what the UI for that looks like but the main point is to utilize React.useRef and attach the ref to your <Camera/> component. Then you may call ref.current.takePictureAsync to capture and access a new image. Look below to see the important relevant snippets for capturing a photo.
import React from 'react'
/* ... other imports
*/
export default CameraScene = () => {
/* ... other state and permission logic
*/
const ref = useRef(null)
const _takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
return (
<Camera style={{flex: 1}} ref={ref}> /* ...
... other ui logic
*/
</Camera>
)
}
Learn more about useRef here https://reactjs.org/docs/hooks-reference.html#useref)
You'll need to add a ref to the Camera class to be able to call it's takePictureAsync function within your own 'handle' method.
cameraRef = React.createRef();
<Camera ref={this.cameraRef}>...</Camera>
Don't forget ".current" when calling the method of the referenced camera.
handlePhoto = async () => {
if(this.cameraRef){
let photo = await this.cameraRef.current.takePictureAsync();
console.log(photo);
}
}
Then simply call your 'handle' method on a touchable element acting as the photo-snap button.
<TouchableOpacity
style={{width:60, height:60, borderRadius:30, backgroundColor:"#fff"}}
onPress={this.handlePhoto} />
You should be able to see the photo logged in your console.
Are you trying to do this on an actual physical device? You can't shoot pictures with an emulator.
How can I solve that, because i can't figure out how to solve. I change the some parts of the code, change the root and add some codes on root,js to try not crash the entire app, but still show me this error. thanks for the help.
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default class Tabs extends Component {
state = {
activeTab: 0
}
render({children} = this.props) {
return (
<div>
<View style={styles.container}>
<View style={styles.tabsContainer}>
{children.map(({ props: { title } }, index) => {
<TouchableOpacity
style={[
// Default style for every tab
styles.tabContainer,
index === this.state.activeTab ? styles.tabContainerActive : []
]}
// Change active tab
onPress={() => this.setState({ activeTab: index }) }
// Required key prop for components generated returned by map iterator
key={index}
>
<Text style={styles.tabText}>
{title}
</Text>
</TouchableOpacity>
<View style={styles.contentContainer}>
{children[this.state.activeTab]}
</View>
</View>
</div>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
tabsContainer: {
flexDirection: 'row',
paddingTop: 30,
},
tabContainer: {
flex: 1,
paddingVertical: 15,
borderBottomWidth: 3,
borderBottomColor: 'transparent',
},
tabContainerActive: {
borderBottomColor: '#FFFFFF',
},
tabText: {
color: '#FFFFFF',
fontWeight: 'bold',
textAlign: 'center',
},
contentContainer: {
flex: 1
}
});
You are missing a closing view tag for the tabs container and you can remove the div:
render({children} = this.props) {
return (
<View style={styles.container}>
<View style={styles.tabsContainer}>
{children.map(({ props: { title } }, index) =>
<TouchableOpacity
style={[
// Default style for every tab
styles.tabContainer,
index === this.state.activeTab ? styles.tabContainerActive : []
]}
// Change active tab
onPress={() => this.setState({ activeTab: index }) }
// Required key prop for components generated returned by map iterator
key={index}
>
<Text style={styles.tabText}>
{title}
</Text>
</TouchableOpacity>
}
</View>
<View style={styles.contentContainer}>
{children[this.state.activeTab]}
</View>
</View>
);
};