React Native get view height - reactjs

Green: full screen view
Red: is a view style absolute bottom:0
Blue: is a view style absolute height:‘100%’
WHAT I WANT
I want find the way to get height of Red View (height isn’t determined).
This height I want to use it for marginBottom for Blue View.
EXPO SNACK
<View style={{
backgroundColor: 'green',
flex:1
}}>
<View style={{
backgroundColor: 'blue',
position:'absolute',
height:'100%',
left:0,
right:0,
top:0,
marginBottom: HEIGHT_OF_RED_VIEW
}}>
</View>
<View style={{
backgroundColor: 'red',
position:'absolute',
left:0,
right:0,
bottom:0,
padding:20
}}>
<Text>Lorem Ipsum</Text>
</View>
</View>

You can try onLayout method of view it will call once the rendeing of your view is completed take a look at example below
<View
onLayout={({ nativeEvent }) => {
const { x, y, width, height } = nativeEvent.layout
setHeight(height)
}}
</View>
you can have height variable defined in your state, and you can use that variable for allocating the margin bottom
You can try code below I have made changes in your expo slack
import { Text, View} from 'react-native';
import {useState} from 'react'
export default function App() {
const [height, setHeight] = useState('')
return (
<View style={{
backgroundColor: 'green',
flex: 1
}}>
<View style={{
backgroundColor: 'blue',
position: 'absolute',
// height: '100%',
left: 0,
right: 0,
top: 0,
bottom: height,// here is you want to have some addintional spacing try adding some value in you hight (ex. height+20)
}}>
</View>
<View
onLayout={({ nativeEvent }) => {
const { x, y, width, height } = nativeEvent.layout
setHeight(height)
}}
style={{
backgroundColor: 'red',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
padding: 20
}}>
<Text>Lorem Ipsum</Text>
</View>
</View>
);
}

Related

Image background which stick to top React Native

I want to achieve the following view, where i have a blue image which is added as a background sticking to the top of the container. Any idea on how this can be achieved in React Native?
P.S. I am using Native Base too in the project
import * as React from 'react';
import { View, Image, Dimensions } from 'react-native';
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
export default function App() {
return (
<View
style={{
flex: 1,
backgroundColor: '#ecf0f1',
}}>
<Image
resizeMode={'contain'}
source={{
uri:
'https://loremflickr.com/cache/resized/65535_51113407033_7bed78119e_z_640_360_nofilter.jpg',
}}
style={{ width: windowWidth, height: 300, position: 'absolute' }}
/>
<View style={{ flex: 1, justifyContent: 'flex-end', paddingBottom: 10 }}>
<View
style={{
width: windowWidth * 0.8,
height: 350,
backgroundColor: 'white',
alignSelf: 'center',
}}
/>
</View>
</View>
);
}

ripple effect leaking at corners as if Pressable button has a borderRadius

I'm using Pressable for buttons after referring this docs pressable docs
Now I want to add ripple effect to the button but it is not working properly.
<Pressable
android_ripple={{color: 'red', borderless: false}}
style={{backgroundColor: 'blue',borderRadius : 10}}>
<Text style={{alignSelf: 'center'}}>Button</Text>
</Pressable>
Ripple effect don't have border radius if button has radius.
it looks awkward that ripple effect corners go out of the curved radius.
Snack demonstrating the problem: https://snack.expo.dev/6U8dxxzLx
Nothing worked for me, So I solved this myself.
pressable should be wrapped in a view
view must have margin not padding
border radius must be on view not on pressable
pressable component must have padding not margin
then add ripple by android_ripple={{color: 'black', borderless: true}} to pressable.
<View style={styles.buttonView}>
<Pressable
onPress={() => {}}
android_ripple={{color: 'black', borderless: true}}
style={styles.loginButton}>
<Text style={styles.buttonText}>Login</Text>
</Pressable>
</View>
buttonView: {
alignSelf: 'stretch',
justifyContent: 'center',
borderRadius: 10,
elevation: 25,
margin: 10,
},
loginButton: {
height: 50,
backgroundColor: '#0f4c75',
padding: 10,
alignItems: 'center',
justifyContent: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
textTransform: 'uppercase',
fontFamily: 'sans-serif-light',
},
Update:-
Floating pressable component with ripple leakage fixed
<View style={{
position: 'absolute',
bottom: 250,
borderRadius: 50,
overflow: 'hidden',
alignSelf: 'center'
}}>
<Pressable
style={{
height: 60,
width: 60,
borderRadius: 50,
backgroundColor: 'red',
justifyContent: 'center',
alignItems: 'center',
elevation: 4,
}}
android_ripple={{
color: 'black',
}}
onPress={() => { console.log('om') }}>
<Text>O</Text>
</Pressable>
</View>
You can wrap pressable into View and pass borderRadius:10 and overflow:'hidden' to View style.
<View style={{ borderRadius: 10, overflow: 'hidden' }}>
<Pressable
android_ripple={{ color: 'red', borderless: false, }}
style={{ backgroundColor: 'blue', borderRadius: 10 }}>
<Text style={{ alignSelf: 'center' }}>Button</Text>
</Pressable>
</View>
Turns out the other solution don't cover all edge cases... I needed to wrap the view 2 times to manage elevation and shadow on iOS. (The button is in absolute position also).
export const PNFloatingButton = ({ children, ...props }) => {
return (
<View style={thisStyles.shadow}>
<View style={thisStyles.parentContainer}>
<Pressable style={thisStyles.button}
android_ripple={{ borderless: false, color: "#0F0" }}>
<Text>Button</Text>
</PNPressable>
</View>
</View>
)
}
const thisStyles = StyleSheet.create({
shadow: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.4,
shadowRadius: 4,
width: 56,
height: 56,
position: 'absolute',
elevation: 2,
right: 0,
bottom: 0,
},
button: {
padding: 4,
backgroundColor: "#F00",
},
parentContainer: {
// Pressable has issues with borderRadius on the ripple on android
overflow: "hidden",
borderRadius: 40,
},
})
For those that are using the Button element I found a simple workaround. Since this problem happens always when border od the button is changed (like became rounded) I just pass to the containerStyle of the button all the props + my button border radius. I add also the overflow property with hidden attribute.
Some little context: myButtonStyle is the style I want to apply to that button. If it's null, I'll apply a default style. Same for myContainerStyle.
The function inplemented before return():
function fixRippleBorder() {
const borderRadius = myButtonStyle
? EStyleSheet.flatten(myButtonStyle).borderRadius
: EStyleSheet.flatten(defaultButtonStyle).borderRadius;
return {
borderRadius: borderRadius,
overflow: 'hidden',
};
}
The button itself:
<Button
containerStyle={[
myContainerStyle ?? defaultContainerStyle,
fixRippleBorder(),
]}
buttonStyle={myButtonStyle ?? defaultButtonStyle}
...
You can add the style overFlow: 'hidden' to a view that wraps around your pressable object as long as that view also contains your borderRadius. Below is an example of a file I worked on recently that does this
import React from 'react';
import {Pressable, View, Text, StyleSheet, Platform} from 'react-native';
export default function CategoryGridTile({title, color, onPress}) {
return (
<View style={styles.gridItem}>
<Pressable
android_ripple={{color: '#ccc'}}
style={({pressed}) => [
styles.button,
pressed ? styles.buttonPressed : null,
]}
onPress={onPress}>
<View style={[styles.innerContainer, {backgroundColor: color}]}>
<Text style={styles.title}>{title}</Text>
</View>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
gridItem: {
flex: 1,
margin: 16,
height: 150,
borderRadius: 8,
elevation: 4,
backgroundColor: 'white',
shadowColor: 'black',
shadowOpacity: 0.25,
shadowOffset: {width: 0, height: 2},
shadowRadius: 8,
overflow: 'hidden',
},
button: {
flex: 1,
},
buttonPressed: {
opacity: 0.5,
},
innerContainer: {
flex: 1,
padding: 16,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontWeight: 'bold',
fontSize: 18,
},
});

React Native Position Absolute Button doesn't work

clicking this position absolute button is not working when I touch it.
code
render() {
return (
<Container>
<View style={{ borderColor: '#fff',height:'30%'}}>
<ImageBackground
source={{uri: 'https://i.ibb.co/TwdZhVM/image-5.png'}}
style={styles.singleCategoryViewBackgroundImage} >
</ImageBackground>
<View style={styles.overlay}>
<Thumbnail large
style={styles.profileImage}
source={{uri:"https://firebasestorage.googleapis.com/v0/b/weddi-4c344.appspot.com/o/Laser%20%26%20Beauty%20Centers%2FDivine%20beauty%20clinics%2FLogo.jpg?alt=media"}} />
<View style={styles.titleContent}>
<Text style={styles.titleText}> Divine Beauty Center</Text>
<Text note>Cairo - Naser City</Text>
<View style={{flexDirection: 'row'}}>
<Button rounded style={styles.actionButton}
onPress={() =>
ActionSheet.show(
{
options: BUTTONS,
cancelButtonIndex: CANCEL_INDEX,
destructiveButtonIndex: DESTRUCTIVE_INDEX,
title: "Testing ActionSheet"
},
buttonIndex => {
this.setState({ clicked: BUTTONS[buttonIndex] });
}
)}
>
<Icon ios="ios-pin" android="md-pin" color="#1a1917"/>
</Button>
<Button rounded style={styles.actionButton}>
<Icon ios="ios-call" android="md-call" color="#1a1917"/>
</Button>
</View>
</View>
</View>
</View>
<Content style={{paddingHorizontal: '6%', marginTop: '20%', height: '20%'}}>
</Content>
</Container>
styles
overlay: {
alignItems: 'center',
height: '65%',
backgroundColor: colors.offWhite,
position: 'absolute',
top: '68%',
left: 0,
right: 0,
bottom: 0,
zIndex: 1,
marginHorizontal: '6%',
borderRadius: 10/2,
opacity: 0.95,
},
singleCategoryViewBackgroundImage: {
width: '100%',
height: '100%',
justifyContent:'center',
alignContent:'center',
},
profileImage: {
position: 'absolute',
top: '-38%',
left: '36%',
right: 0,
bottom: 0,
zIndex: 2,
alignSelf: 'center',
overflow: 'hidden',
borderWidth: 2,
borderColor: '#b42334',
width: 100,
height: 100,
borderRadius: Platform.OS === 'ios' ? 0 : 150/2,
},
titleContent: {
zIndex: 40,
marginVertical: '15%',
alignItems: 'center',
},
titleText: {
fontFamily: 'Roboto',
fontWeight: 'bold',
},
actionButton: {
width:50,
zIndex: 50,
margin: '2%',
backgroundColor: colors.red,
}
I tried replacing the native-base button with react native button still doesn't work but when I make the button outside this position absolute mess it works great but I like how it looks inside this view so any ideas how can I get it to work that way?
From https://www.w3schools.com/cssref/pr_pos_z-index.asp: "Note: z-index only works on positioned elements (position: absolute, position: relative, position: fixed, or position: sticky)." Since actionButton doesn't specify a position attribute, it's position is the default position value (static) and z-index does not apply. So your absolutely positioned Views are on top of your button.

Aligning image over 2 background views

I have two background views with different colors. Both their own size.
Over these backgrounds, I need an image aligned on the left, over both other views.
how can i accomplish this?
export default class MainScreen extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.rectangle1}>
<Image
source={require('../assets/dame.png')}
style = {styles.image}/>
</View>
<View style={styles.rectangle2}>
</View>
</View>
)
}
}
Added sample image what to accomplish:
besides the use of absolute positioning, here is another tip to achieve a similar layout on most devices is the use of Dimensions and constants for the width and height of the devices that.
based on the information provided, here is how my design will look like:
////import { Dimensions } from react-native;
const { width: deviceWidth, height: deviceHeight } = Dimensions.get('window');
export default class MainScreen extends Component {
render() {
return (
<View style={{ backgroundColor: 'magenta', height: deviceHeight }}>
<View
style={{
width: deviceWidth * 0.44,
alignSelf: 'flex-start',
}}
>
<Image
source={{ uri: 'https://ui-avatars.com/api/?name=Alice' }}
style={{ width: '100%', height: '100%' }}
/>
</View>
<View
style={{
backgroundColor: 'black',
position: 'absolute',
width: deviceWidth * 0.56,
height: deviceWidth * 0.56,
alignSelf: 'flex-end',
opacity: 0.7
}}
/>
<View
style={{
backgroundColor: 'yellow',
width: deviceWidth * 0.7,
height: deviceWidth * 0.1,
position: 'absolute',
top: deviceHeight * 0.8,
alignSelf: 'center',
opacity: 0.8,
}}
/>
<View
style={{
backgroundColor: 'blue',
width: deviceWidth,
height: deviceWidth * 0.14,
position: 'absolute',
bottom: 0,
alignSelf: 'flex-end',
opacity: 0.8,
}}
/>
</View>
)
}
}
i suggest you keep experimenting to find out which method works best for your application.

React-native-router-flux custom navbar renders at the bottom of the view

As stated if I use a custom navBar in react-native-router-flux, and style it with NO position stlyle tag, then the navBar renders at the bottom of the screen...
So, I tried setting style with position: 'absolute', top: 0, right:0, left: 0 and my navBar disappears :( Any suggestions?
Router.js
<Scene
key="api"
hideNavBar={false}
navBar={HeaderWithIcon}
component={APITest}
/>
HeaderWithIcon.js
import React, {Component} from 'react';
import { View, Image, Text } from 'react-native';
import { menuStyles } from './styles';
class HeaderWithIcon extends Component {
constructor(props) {
super(props);
}
render() {
const icon =
require('../../assets/images/myImg.png');
return (
<View>
<View style={[menuStyles.view, this.props.style]}>
<Image source={icon} style={[menuStyles.icon, this.props.iconStyle]} />
</View>
</View>
);
}
};
export default HeaderWithIcon;
styles.js
import { HEADER } from '../../global/margins';
import { PRIMARY_COLOUR, SHADOW_COLOUR } from '../../global/colours';
export const menuStyles = {
view: {
flexDirection: 'row',
//height: HEADER.height,
width: null,
backgroundColor: PRIMARY_COLOUR,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
// paddingTop: HEADER.padding,
shadowColor: SHADOW_COLOUR,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
position: 'absolute',
top: 0,
left: 0,
right: 0
},
icon: {
width: HEADER.icon,
height: HEADER.icon,
alignSelf: 'center'
}
};
<View style={styles.navbarStyle}>
<View style={styles.navContainer}>
<TouchableOpacity onPress={()=>Actions.get('drawer').ref.toggle()}>
<Image source={require('../../img/menu.png')} resizeMode="contain" color="white" style={styles.menu} />
</TouchableOpacity>
<View style={styles.navbarTitle}>
<Text style={styles.title}>NavBar</Text>
</View>
<TouchableWithoutFeedback onPress={this.loginUser}>
<View style={styles.signIn}>
<Text style={styles.title}>Login</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
And in order to style it and put it at the top ,remember to give it a position of 'absolute ' and 'top' of 0 :
navbarStyle:{
flex:1,
flexDirection:'row',
alignItems: 'center',
height ,
position: 'absolute', //necessary because if no react-native-router-flux will put navbar at bottom ,bug maybe!
top:0,
width,
backgroundColor: '#009688',
elevation:10
}
PS: for width and height ,better solution is to use Navigator and dimensions :
const {width} = Dimensions.get('window');
const height = Navigator.NavigationBar.Styles.General.NavBarHeight;
Nevermind... I realise that my HeaderView is wrapped in 2 views and I was not styling the outer one but the inner one. Leaving this here as example code for others if allowed.....
Fix:
In HeaderWithIcon.js
return (
<View style={[menuStyles.view, this.props.style]}>
<Image source={icon} style={[menuStyles.icon, this.props.iconStyle]} />
</View>
);

Resources