Is there way to pass touch event from one view to another? - react-native

A have two layers with components on the screen. Upper layer is intercepting all touch events. I need the way to propagate those events to the below layer. Please, look the example below, for now only onUpperViewPressed is outputs. The desired behavior are both: onBellowViewPressed and onUpperViewPressed should be outputed when corresponding views are pressed.
import * as React from 'react';
import { Text, View, SafeAreaView } from 'react-native';
export default function App() {
return (
<SafeAreaView style={{flex: 1}}>
<View style={{ flex: 1, backgroundColor: 'grey' }}>
<Text
onPress={() => console.log('onBellowViewPressed')}
style={{
position: 'absolute',
top: 100,
left: 100,
backgroundColor: 'red',
}}>
Below view
</Text>
<Text
onPress={() => console.log('onUpperViewPressed')}
style={{ flex: 1 }}>
Upper view
</Text>
</View>
</SafeAreaView>
);
}
https://snack.expo.io/jJ1DQDMeR
What I've already tried:
Tried to add 'zIndex: 1' to 'below view', but in that case 'below view' lower view starts to intercept all events.
Tried to play with pointerEvents with no luck.
Also tried react-native-touch-through-view
Any suggestions? Thank you.

Related

react native bottom sheet bottom transparent

import React, { useState } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { BottomSheet } from "react-native-btr";
export default function BottomSheetDemo() {
const [visible, setVisible] = useState(false);
function toggle() {
setVisible((visible) => !visible);
}
return (
<View style={styles.container}>
<TouchableOpacity onPress={toggle}>
<View style={styles.button}>
<Text>Toggle BottomSheet</Text>
</View>
</TouchableOpacity>
<BottomSheet
visible={visible}
onBackButtonPress={toggle}
onBackdropPress={toggle}
>
<View style={styles.card}>
<Text>Place your custom view inside BottomSheet</Text>
</View>
</BottomSheet>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
button: {
backgroundColor: "#fff",
borderWidth: 2,
borderRadius: 50,
padding: 16,
},
card: {
backgroundColor: "#fff",
height: 250,
justifyContent: "center",
alignItems: "center",
},
});
I am using the above code. So my problem is I am getting the bottom sheet as expected. And I want that it will 100px above the bottom. That is also working fine. But the thing is I am getting bottom and at top blur black background. But I want on the bottom margin-bottom 100. It will not show that black ground. I will start from marginbottom:100. But the bottom background will be transparent and I can do click on the bottom items.
TLDR: working source code. Be aware, this solution might get
messy.
If we see the source code of react-native-btr then we will see that the BottomSheet component is actually a Modal component from react-native-modal passing 4 basic props to react-native-modal as shown below
<Modal
isVisible={visible}
onBackButtonPress={onBackButtonPress}
onBackdropPress={onBackdropPress}
style={{ justifyContent: "flex-end", margin: 0 }}
>
{children}
</Modal>
So, what I did is, extract all the source code from react-native-modal which is just five files and modify this source code. I have put a new prop for react-native-modal called bottomSpacing, so that user can change the bottom spacing.
Back in app.js the code is looking like this
<Modal
testID={"modal"}
isVisible={isModalVisible}
style={{ justifyContent: "flex-end", margin: 0 }} // this line was using in react-native-btr source
bottomSpacing={BOTTOM_SPACING}>
<View style={styles.card}>
<Text>Place your custom view inside BottomSheet</Text>
</View>
</Modal>
Here, BOTTOM_SPACING is used both in bottomSpacing props and the styles.card as following
card: {
marginBottom: BOTTOM_SPACING,
...
}
Bonus: Now you can use all the features of react-native-modal such as changing the color of the shadow and the opacity of that shadow etc.

Cant Use onPress() Events on Animated.View set to Position: Absolute - React Native

I made a scrollable header for an app I am building for a company. When I try to use a TouchableHighlight to trigger an onPress() event it does not register because the position key is set to 'absolute':
Ive tried the following after scouring the internet for hours:
-raising the zIndex/elevation
-making sure TouchableHighlight is imported from 'react-native'
-Animated library from 'react-native-reanimated'
When I comment out the position:absolute in the main header it works perfectly, but the header leaves a view behind it when it scrolls up that I cant lay a view on top of.
I'm running out of options here...any help would be GREATLY appreciated!
<Animated.View style={globalStyles.tabNavbar}>
<TouchableHighlight
style={{backgroundColor: 'white', height: 50, width: 100, zIndex: 99999, elevation: 99999}}
underlayColor="#eee"
onPress={() => {
console.log('PLZ WORK');
this.updateTabs('projectsTab');
}} >
<Animated.View style={{ alignItems: 'center'}}>
<Text onPress={() => { this.updateTabs('projectsTab');}} style={{fontWeight: 'bold'}}>Close</Text>
</Animated.View>
</TouchableHighlight>
<TouchableHighlight
style={{color: 'white'}}
underlayColor="#eee"
onPress={() => {
console.log('PLZ WORK');
this.updateTabs('notificationTab');
}} >
<View style={{alignItems: 'center'}}>
<Text style={{fontWeight: 'bold'}}>Close</Text>
</View>
</TouchableHighlight>
</Animated.View>
For those wondering, If you alter the View order of the Components to where the component with position: absolute is last, the buttons will work. In this situation I had to put my header last in the order even though it appears at the top of my screen.

Disable TouchableOpacity for nested View

I've a touchable opacity, and I have a few views inside it. I have one specific view that I don't want for it to be clickable. How can I achieve this?
The specific view that you don't want for it to be clickable should be "TouchableOpacity" but have activeOpacity={1} . In this way parent TouchableOpacity will not work and activeOpacity={1} will make it like disable
Complete code
import React, { Component } from "react";
import { TouchableOpacity, View, Text } from "react-native";
export default class App extends Component {
render() {
return (
<View style={{ flex: 1, margin: 50 }}>
<TouchableOpacity
style={{ backgroundColor: "red", width: 250, height: 250 }}
>
<TouchableOpacity
style={{
backgroundColor: "green",
width: 100,
height: 100,
margin: 20,
alignItems: "center",
justifyContent: "center"
}}
activeOpacity={1}
>
<Text>No Click Area</Text>
</TouchableOpacity>
</TouchableOpacity>
</View>
);
}
}
App Preview
Go in that view and add a prop => pointerEvents:none
<View pointerEvents="none" />
Please refer here
I don't know what conditions you're talking about, but if you want to do what you want, you can use the status value. To deactivate a button when displaying a screen, change the status value when rendering the screen, or change it when you press a button. The examples are attached together.
Example
import * as React from 'react';
import { Text, View, StyleSheet,TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
export default class App extends React.Component {
constructor(props){
super(props);
this.state={
disabled: false
}
}
componentDidMount(){
this.setState({ disabled: true})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Change code in the editor and watch it change on your phone! Save to get a shareable url.
</Text>
<TouchableOpacity style={{width:"100%",height:20,alignItems:"center",backgroundColor:"blue"}} onPress={() => alert("touch")} disabled={this.state.disabled}>
<Text>Touch </Text>
</TouchableOpacity>
<TouchableOpacity style={{width:"100%",height:20,alignItems:"center",backgroundColor:"red"}} onPress={() => this.setState({disabled:true})}>
<Text>disabled</Text>
</TouchableOpacity>
</View>
);
}
}
Another way you could do it is to wrap you View which you don't want to be clickable with TouchableWithoutFeedback.
export default class App extends React.Component {
render() {
return (
<View style={{flex: 1, justifyContent: 'center'}}>
<TouchableOpacity style={{backgroundColor: "blue", width: 300, height: 300}}>
<TouchableWithoutFeedback>
<View style={{backgroundColor: "yellow", width: 100, height: 100}}>
<Text>Hello</Text>
</View>
</TouchableWithoutFeedback>
</TouchableOpacity>
</View>
);
}
}
As #AnaGard suggested, the key to having a press free view inside a pressable container is to make a pressable inner view without an onPress value.
Better than using TouchableOpacity is using the Pressable component, which the ReactNative's documentation suggests is more future-proof.
Therefore, an updated answer to this question might be as follows:
<View>
<Pressable
style={{ width: 500, height: 250 }}
onPress={() => onClose()}
>
<Pressable style={{ height: 100, width: 200 }}>
<View>
<Text>Your content here</Text>
</View>
</Pressable>
</Pressable>
</View>
Some references:
Pressable
TouchableOpacity

How to position a View at the bottom of a ScrollView?

I would like to create a screen with a ScrollView with some input fields in it, and a Button (actually 2 buttons in a normal View) at the bottom of the screen. This should be pretty easy, BUT, I want to position my button at the very bottom of the screen even if there isn't enough element in the ScrollView to fill the whole screen. I could use absolute positioning, BUT my ScrollView can be bigger (higher) then my screen, and in this case, the button should be at the end of the ScrollView. (So it would be out of the screen, which means the user should scoll down to see the button).
I've tried a lot of things, but the Button always comes right after the other elements inside the ScollView.
In the picture the scrollview has a blue border, and the normal view which contains the buttons has black border.
Any suggestion is appreciated.
Found the best solution.
The key is the contentContainerStyle property (doc: https://facebook.github.io/react-native/docs/scrollview#contentcontainerstyle). "These styles will be applied to the scroll view content container which wraps all of the child views. "
After setting flexGrow: 1, justifyContent: 'space-between', flexDirection: 'column' in contentContainerStyle, one can set justifyContent: 'flex-start' for the upper container view with the text, and justifyContent: 'flex-end' for the lower container view with the buttons.
<ScrollView
contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between', flexDirection: 'column' }}
style={{ backgroundColor: 'white', paddingBottom: 20 }}
>
<View style={{ flex: 1, justifyContent: 'flex-start' }}>
<Text>Some text with different length in different cases, or some input fileds.</Text>
</View>
<View style={{ flex: 1, justifyContent: 'flex-end' }}>
<View>
<TouchableOpacity style={[commonStyles.greenButton, styles.buttonPadding]}>
<Text style={commonStyles.greenButtonTitle}>Next</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={styles.cancelButtonContainer}>
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>;
<ScrollView
contentContainerStyle={{
flexGrow: 1,
justifyContent: 'flex-end',
}}>
</ScrollView>
I found a workaround for this.
The main idea is to set the height of the View with the texts and input fields to match exactly the height of the screen minus the View which contains the buttons.
The challenge is to calculate this height. Abhishek Kumar's answer helped me (https://stackoverflow.com/a/41398953/4109477) See my code below:
import { View, ScrollView, Text, TouchableOpacity, Dimensions } from 'react-native';
const screenHeight = Dimensions.get('window').height;
class MyClass extends React.Component {
constructor(props) {
super(props);
this.state = {buttonViewHeight: 0};
}
find_dimesions(layout){
this.setState({buttonViewHeight: layout.height});
}
render() {
return (
<View style={{minHeight: screenHeight, flex: 1, alignItems: 'center'}}>
<ScrollView style={{minHeight: screenHeight}}>
<View style={{minHeight: screenHeight}}>
<View style={{minHeight: screenHeight - this.state.buttonViewHeight, borderWidth: 2, borderColor: 'red', alignItems: 'center', flex: 1}]}>
<Text>Some text with different length in different cases, or some input fileds.</Text>
</View>
<View onLayout={(event) => { this.find_dimesions(event.nativeEvent.layout) }} style={{paddingBottom: 50, flex: 1, borderWidth: 2, borderColor: 'green'}}>
<TouchableOpacity
style={[commonStyles.greenButton, styles.buttonPadding]} >
<Text style={commonStyles.greenButtonTitle}>Next</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.cancelButtonContainer} >
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</View>
}
}
On the picture below you can see the result
This solution will cause an animation like effect as the view renders multiple times, because of the changing of the View's height. The button's View "fade in" as you open the screen.
If you find a better solution please let me know!

TouchableHighlight has opacity default in React Native

Without touching (and by default) Touchable highlight is giving me a semi-transparent button!
<LoginButton ref={btn => { this.btn = btn; }} onPress={this._executeLoginQuery} text='Sign in'></LoginButton>
rendered in LoginButton as
render () {
const { icon} = this.props;
return (
<TouchableHighlight style={styles.button} onPress={this.props.onPress}>
<View
style={{
flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.buttonText}>{this.getText()}</Text>
</View>
</TouchableHighlight>
)
}
}
with the style
button: {
height: 45,
borderRadius: 100,
marginHorizontal: Metrics.section,
marginVertical: Metrics.baseMargin,
backgroundColor: Colors.blueButton,
justifyContent: 'center',
overflow:'hidden',
opacity: 1.0,
},
Giving the result as:
And as you can see background "waves" are coming through - not just through the button but the parent white background too!
How can I stop this?
From react native docs:
The underlay comes from wrapping the child in a new View, which can affect layout, and sometimes cause unwanted visual artifacts if not used correctly, for example if the backgroundColor of the wrapped view isn't explicitly set to an opaque color.
Can you tell me whats the value of Colors.blueButton. If there is opacity in any child under TouchableHighlight then please try removing it.
2nd Way
You can use TouchableOpacity and control its opacity by using activeOpacity prop.
<TouchableOpacity activeOpacity={0.8}>
//...login Button view
</TouchableOpacity>