React Native - Image bigger than parent - react-native

Like in tinder, i have a screen with 3 Image Pickers. When I pick an image from the gallery, I want it to persist inside the TouchableOpacity container. However, the image overlaps the container.
I have been been trying various things mentioned here but the image still overlaps like in the image below:
What is the problem?
/* eslint-disable react-native/no-inline-styles */
import React, { useState } from 'react'
import {
Text,
View,
Image,
Button,
TouchableOpacity,
StyleSheet,
Dimensions,
} from 'react-native'
import ImagePicker from 'react-native-image-crop-picker'
import { FontAwesomeIcon } from '#fortawesome/react-native-fontawesome'
import { faPlusCircle } from '#fortawesome/free-solid-svg-icons'
import Colors from '../../../constants/Colors'
const { width: WIDTH } = Dimensions.get('window')
const ProfileEdit = () => {
const [photo, setPhoto] = useState(null)
const handleChoosePhoto = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
}).then((image) => {
setPhoto({
uri: image.path,
width: image.width,
height: image.height,
mime: image.mime,
})
console.log(image)
})
}
return (
<View style={styles.main}>
<View style={styles.button_row}>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<Image source={photo} style={styles.photo} />
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
</View>
</View>
)
}
const styles = StyleSheet.create({
main: {
flex: 1,
},
icon: {},
button: {
height: `${100}%`,
backgroundColor: Colors.defaultWhite,
padding: 2,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 3,
},
buttonWrapper: {
width: WIDTH / 3,
padding: 10,
},
button_row: {
flex: 0.3,
flexWrap: 'wrap',
flexDirection: 'row',
},
photo: {
width: WIDTH / 2,
height: WIDTH / 2,
// borderRadius: 3,
resizeMode: 'contain',
},
})
export default ProfileEdit

correct answer as described here
"However because images will try to set the width and height based on the actual size of the image, you need to override these style properties"
<Image
style={{
flex: 1,
alignSelf: 'stretch',
width: undefined,
height: undefined
}}
source={require('../../assets/images/onboarding-how-it-works.png')}
/>

Instead of calculating image width try this:
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<View style={styles.photoContainer}>
<Image source={photo} style={styles.photo} />
</View>
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
const styles = StyleSheet.create({
photoContainer: {
// flex: 1
},
photo: {
height: WIDTH / 2,
width: '100%',
resizeMode: 'contain'
}
});

I prefer ImageBackground instead. Below is the code to achieve you the desired View:
<ImageBackground
imageStyle={styles.image}
style={styles.imageContainer}
source={{uri: this.state.imageUri}}>
<TouchableOpacity onPress={this._pickImage} style={{
height: WIDTH/2,
width: WIDTH/2,
justifyContent: "center",
alignItems: "center"}}>
<Entypo name="camera" color={Colors.GREY} size={35}/>
</TouchableOpacity>
</ImageBackground>

Related

White background in react-native-view-shot

I am using react-native-view-shot to save a screenshot of a view, whose height is not initially defined, as I am using padding and setting the height of the view using the onLayout method.
The problem is that, when the view has an initial fixed height, the screenshot taken does not have a white background, which is what I want. However, when I set the height when the onLayout is invoked, the screenshot has a white background.
Here's my code:
const [height, setHeight] = useState();
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
ref={contentRef}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={styles.text}>This is a test using padding</Text>
</View>
https://snack.expo.dev/#pietroputelli/react-native-view-shot
=========== EDIT ===========
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<View ref={shotRef} style={{ backgroundColor: "transparent" }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</View>
<Button
onPress={() => {
captureRef(shotRef, {
format: "png",
quality: 0.8,
}).then(
async (uri) => {
await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
I can able to generate the same from viewshot:
take another view and give a reference to that view and generate a screenshot from that. might be its issue of reference. Please check the below screenshot.
<View ref={viewshotRef}
style={{
// whatever you want to add as per your requirement
}} >
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: DEVICE_WIDTH / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: 'white' }}>This is a test using padding</Text>
</View>
</View>
For base64:
import * as MediaLibrary from "expo-media-library";
import React, { useRef, useState } from "react";
import {
Button,
Dimensions,
StyleSheet,
Text,
View,
} from "react-native";
import ViewShot, { captureRef } from "react-native-view-shot";
const { width } = Dimensions.get("window");
export default function ViewCapture() {
const contentRef = useRef();
const [height, setHeight] = useState(undefined);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "transparent" }}>
<ViewShot ref={contentRef} options={{ result: 'base64' }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</ViewShot>
{/* </View> */}
<Button
onPress={() => {
contentRef.current.capture().then((url) => {
console.log("on success", "data:image/jpeg;base64,", url)
// await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
);
}
For File:
You need to set the result as tmpfile so you will get file uri in the callback
<ViewShot ref={contentRef} options={{ result: 'tmpfile' }}>
I hope it will work!

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

What's my mistake when using ternany operator with isTextInputFocused property in react-native?

I have a textInput. When i click on the textInput, i want "Search Icon" to be passive, but when i dont click on the textInput, i want "Search Icon" to be active,
Its strangely not doing what I want. As I look at my code, it looks correct, but i must have something wrong because it doesnt work as i wanted.(not giving an error tho)
App.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, isTextInputFocused } from 'react-native';
import Search from "./src/icons/search.svg";
import {Loupe} from "./src/icons/loupe.png";
const App = () => {
return (
<View style={styles.container}>
<View style={styles.firstPart}>
<Text style={styles.date}>Saturday, Feb 21</Text>
<View style={styles.package}>
<Text style={styles.packageText}>Your Package</Text>
<View style={styles.plusIcon}>
<Text style={styles.textPlus}>+</Text>
</View>
</View>
{isTextInputFocused ? (<Search height={25} width={20} fill={"#00FF00"} style={{ position: "absolute", top: 90, left: 30, zIndex: 0 }} />):
(<Search height={25} width={20} fill={"#A9A9A9"} style={{ position: "absolute", top: 90, left: 25, zIndex: 20 }}/>)}
<TextInput style={styles.input} placeholder="Search" placeholderTextColor="gray"></TextInput>
</View>
{/* { !isTextInputFocused && (<View>
<Search height={30} width={30} fill={"#00FF00"} />
</View>)} */}
</View>
);
};
const styles = StyleSheet.create({
container:{
margin:10,
},
firstPart:{
backgroundColor: "mediumblue",
borderTopRightRadius:40,
borderTopLeftRadius:40,
padding:20,
},
date:{
fontSize:15,
color:"white",
},
package:{
flexDirection: "row",
justifyContent: "space-between",
},
packageText: {
fontSize:25,
color:"white",
flexDirection: "column",
},
plusIcon: {
height:25,
width:25,
borderRadius:25,
backgroundColor:"blue",
marginTop:5,
},
textPlus: {
fontSize:25,
fontWeight:"bold",
color:"white",
marginLeft:5,
},
input: {
height:40,
borderWidth:.5,
borderRadius:10,
marginVertical:10,
backgroundColor:"darkslateblue",
padding:5,
}
});
export default App;
You can use the TextInput component's onFocus, onBlur props to focus information on an input. Sample code is available below.
const [text, setText] = useState("")
const [inputFocus, setFocus] = useState(false)
return (
<View style={styles.container}>
<Text>
{inputFocus ? "Focused" : "Not Focused"}
</Text>
<TextInput
value={text}
onChangeText={setText}
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
style={{
width: "100%",
height: 44,
backgroundColor: "gray"
}}
/>
</View>
)

KeyboardAvoidingView doesn't work properly on my app

I searched a lot about this issue and I don't know what to do now so I decided to ask here and any help would be appreciated. in my register screen I have some input that user must fill and for the last one I need to avoid keyboard overlay. so I used KeyboardAvoidingView component to solve this but as you can see in the screenshot the device keyboard still overlay my input (birth date).
here is my code for this screen :
import React, { useState } from 'react';
import { View, Text, KeyboardAvoidingView, Image, Dimensions, PixelRatio, StyleSheet } from 'react-native';
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';
import { COLORS } from '../Constants/COLORS';
import PrimaryButton from '../Components/PrimaryButton';
import PrimaryInput from '../Components/PrimaryInput';
import CheckBox from '../Components/CheckBox';
const Register = (props) => {
const [lisenceTerm, setLisenceTerm] = useState(true);
const [privacyPolicy, setPrivacyPolicy] = useState(true);
return (
<View style={styles.screen}>
<View style={styles.imageContainer}>
<Image style={styles.image} source={require('../assets/Img/office_5.jpg')} />
</View>
<View style={styles.loginContainer}>
<View style={styles.headerContainer}>
<Text style={styles.headerText}>Join us</Text>
</View>
<KeyboardAvoidingView style={styles.inputContainer}>
<PrimaryInput placeholder='Name Surname' />
<PrimaryInput placeholder='Your Email' />
<PrimaryInput placeholder='Phone Number (05***)' />
<PrimaryInput placeholder='Birth Date' />
</KeyboardAvoidingView>
<View style={styles.checkBoxContainer}>
<CheckBox text='Kvkk sözleşmesini okudum, kabul ediyorum' active={lisenceTerm} onPress={() => setLisenceTerm(!lisenceTerm)} />
<CheckBox text='Kullanıcı sözleşmesini okudum, kabul ediyorum' active={privacyPolicy} onPress={() => setPrivacyPolicy(!privacyPolicy)} />
</View>
<View style={styles.buttonsContainer}>
<PrimaryButton
buttonStyle={styles.button}
textStyle={styles.buttonText}
title='Register' />
</View>
</View>
</View>
)
}
const styles = StyleSheet.create({
screen: {
flex: 1,
},
imageContainer: {
width: '100%',
height: '34%'
},
image: {
width: '100%',
height: '100%'
},
loginContainer: {
backgroundColor: 'white',
width: '100%',
height: '71%',
position: 'absolute',
zIndex: 1,
bottom: 0,
borderTopLeftRadius: 40,
borderTopRightRadius: 40,
alignItems: 'center'
},
buttonsContainer: {
width: '100%',
justifyContent: 'center',
alignItems: 'center'
},
button: {
justifyContent: 'center',
alignItems: 'center',
width: '75%',
paddingVertical: '3%',
marginTop: hp(1.2),
borderRadius: hp(3.5),
backgroundColor: COLORS.royalBlue
},
buttonText: {
fontSize: hp(3.2),
fontFamily: 'BalooPaaji2ExtraBold',
color: 'white'
},
arrow: {
right: 20
},
inputContainer: {
width: '100%',
justifyContent: 'center',
alignItems: 'center',
marginBottom: hp(1),
},
headerContainer: {
marginTop: hp(3),
marginBottom: hp(2),
width: '75%',
},
headerText: {
fontSize: hp(3.5),
fontFamily: 'BalooPaaji2Bold',
color: COLORS.royalBlue
},
checkBoxContainer: {
width: '70%',
justifyContent: 'flex-start',
marginBottom: hp(1)
}
})
export default Register;
and here below is the result. btw I used behavior and keyboardVerticalOffset props to control the way this component behave but still same problem.
Your KeyboardAvoidingView component must be on top of render tree
In order to scroll onto your Join us view, you must set a ScrollView in your KeyboardAvoidingView and those component must be on top of renderer.
Try this (based on my iOS / android setup) :
import { KeyboardAvoidingView, ScrollView } from 'react-native';
const Register = (props) => {
const [lisenceTerm, setLisenceTerm] = useState(true);
const [privacyPolicy, setPrivacyPolicy] = useState(true);
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={(Platform.OS === 'ios') ? 'padding' : null}
enabled
keyboardVerticalOffset={Platform.select({ ios: 80, android: 500 })}>
<ScrollView>
<View style={styles.screen}>
<View style={styles.imageContainer}>
<Image style={styles.image} source={require('../assets/Img/office_5.jpg')} />
</View>
<View style={styles.loginContainer}>
<View style={styles.headerContainer}>
<Text style={styles.headerText}>Join us</Text>
</View>
<View style={styles.inputContainer}>
<PrimaryInput placeholder='Name Surname' />
<PrimaryInput placeholder='Your Email' />
<PrimaryInput placeholder='Phone Number (05***)' />
<PrimaryInput placeholder='Birth Date' />
</View>
<View style={styles.checkBoxContainer}>
<CheckBox text='Kvkk sözleşmesini okudum, kabul ediyorum' active={lisenceTerm} onPress={() => setLisenceTerm(!lisenceTerm)} />
<CheckBox text='Kullanıcı sözleşmesini okudum, kabul ediyorum' active={privacyPolicy} onPress={() => setPrivacyPolicy(!privacyPolicy)} />
</View>
<View style={styles.buttonsContainer}>
<PrimaryButton
buttonStyle={styles.button}
textStyle={styles.buttonText}
title='Register' />
</View>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
)
}
you need to install react-native-keyboard-aware-scroll-view by
yarn add react-native-keyboard-aware-scroll-view
and you need to wrap KeyboardAwareScrollView instead of KeyboardAvoidingView
like
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
<KeyboardAwareScrollView>
<View>
...
</View
</KeyboardAwareScrollView>
import { useHeaderHeight } from "#react-navigation/elements"
import {
Keyboard,
Platform,
TouchableWithoutFeedback,
View,
KeyboardAvoidingView
} from "react-native"
const Chat = () => {
// This is the crucial variable we will place it in
// KeyboardAvoidingView -> keyboardVerticalOffset
const headerHeight = useHeaderHeight()
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView
style={{ position: "absolute", bottom: 0, left: 0, right: 0 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
// If you want the input not to stick exactly to the keyboard
// add a const here for example headerHeight + 20
keyboardVerticalOffset={headerHeight}
>
<View style={{ flex: 1, justifyContent: "flex-end" }}>
<InputWrapper>
<RawInput />
</InputWrapper>
</View>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
)
}
Also take a look at this article it might be helpful: https://medium.com/#nickyang0501/keyboardavoidingview-not-working-properly-c413c0a200d4

React native elements header background image

I made a header using react native elements, and I want to add background image into it. Is there anyway to do it?
I am using react-native-elements: "^0.19.1"
Here is my header code
render() {
return (
<View style={{ paddingTop: Constants.statusBarHeight, backgroundColor: color.light_grey }}>
<Header
leftComponent={this.props.left ? this.props.left : <IconWrapper name='menu' color='black' size={28} style={styles.icon} onPress={() => Actions.drawerOpen()} />}
centerComponent={<Text style={styles.headerText}>{this.props.title}</Text>}
rightComponent={this.props.right ? this.props.right : <IconWrapper name='handbag' type='simple-line-icon' color='black' size={28} style={styles.icon} onPress={() => Actions.BagContents()} />}
outerContainerStyles={[styles.headerOuterContainer, { height: 0.08 * windowHeight() }]}
/>
</View>
)
}
}
You can always create your own <Header/> component, probably will take you more time but you will be able to understand it and edit it as you like. I created a simple Header component to show you how you can accomplish adding a background image to your header. See the snack #abranhe/stackoverflow-56729412
Header.js
import React, { Component } from 'react';
import { View, TouchableOpacity, StyleSheet, Dimensions, ImageBackground } from 'react-native';
export default class Header extends Component {
renderContent() {
return (
<View style={styles.content}>
<View style={styles.left}>{this.props.left}</View>
<View style={styles.center}>{this.props.center}</View>
<View style={styles.right}>{this.props.right}</View>
</View>
);
}
renderHeaderWithImage() {
return (
<ImageBackground style={styles.container} source={this.props.imageSource}>
{this.renderContent()}
</ImageBackground>
);
}
renderHeaderWithoutImage() {
return (
<View style={[{ backgroundColor: '#f8f8f8' }, styles.container]}>
{this.renderContent()}
</View>
);
}
render() {
return this.props.image
? this.renderHeaderWithImage()
: this.renderHeaderWithoutImage();
}
}
const styles = StyleSheet.create({
container: {
top: 0,
position: 'absolute',
width: Dimensions.get('window').width,
backgroundColor: '#f8f8f8',
borderBottom: 1,
borderColor: '#f8f8f8',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.5,
},
content: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: Dimensions.get('window').height * 0.03,
height: Dimensions.get('window').height * 0.045,
},
left: {
marginHorizontal: 5,
},
center: {
marginHorizontal: 5,
},
right: {
marginHorizontal: 5,
},
});
and then on when you want to use the Header component you can set the image prop to true, eg:
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import Header from './components/Header';
export default () => {
return (
<View>
<Header
image
imageSource={{ uri: 'https://yourimage.png' }}
left={<Ionicons name="md-arrow-round-back" size={25} />}
center={<Text>Projects</Text>}
right={<Ionicons name="ios-camera" size={25} />}
/>
</View>
);
};
and then if you set the image prop to false you will remove the image from the background.
<Header
image={false}
imageSource={{ uri: 'https://yourimage.png' }}
left={<Ionicons name="md-arrow-round-back" size={25} />}
center={<Text>Projects</Text>}
right={<Ionicons name="ios-camera" size={25} />}
/>
There is ReactNative's component ImageBackground you can use it.
like this,
<ImageBackground source={...} style={{width: '100%', height: '100%'}}>
<Header
leftComponent={this.props.left ? this.props.left : <IconWrapper name='menu' color='black' size={28} style={styles.icon} onPress={() => Actions.drawerOpen()} />}
centerComponent={<Text style={styles.headerText}>{this.props.title}</Text>}
rightComponent={this.props.right ? this.props.right : <IconWrapper name='handbag' type='simple-line-icon' color='black' size={28} style={styles.icon} onPress={() => Actions.BagContents()} />}
outerContainerStyles={[styles.headerOuterContainer, { height: 0.08 * windowHeight() }]}
/>
</ImageBackground>
You can always style it your way.
assuming you are using react-navigation
You need to add a custon header component in navigationOptions,
import { Header } from 'react-navigation';
static navigationOptions = ({ navigation }) => {
return {
header: (props) => <View >
<LinearGradient
style={{ height: '100%', width: '100%', position: 'absolute' }}
start={{ x: 0, y: 1 }} end={{ x: 1, y: 0 }} colors={['#1A9EAE', '#4EAE7C']}
/>
<Header {...props} style={{ backgroundColor: Colors.transparent }} />
</View>,
}
}
This works for me.
<Header
backgroundImage={require("../../assets/images/btn-header-background.png")}
centerComponent={{
text: i18n.t("stats.title"),
style: { fontFamily: "FunctionLH", fontSize: 30, color: "#fff" }
}}
containerStyle={{
backgroundColor: "transparent",
justifyContent: "space-around"
}}
statusBarProps={{ barStyle: "light-content" }}
/>