How to make scroll view scroollabe inside panGestureHandler? - react-native-reanimated

I am using a react-native-gesture-handler library with reanimated and react-native-redash. I want to make scrollView scrollable which is nested inside panGestureHandler but all the interactions are handled by panGestureHandler, so scrollView is not working. Here is my code.
VideoModal.js file
import Animated , {useCode , cond , eq , set , add, block, interpolate, Extrapolate} from "react-native-reanimated"
import {PanGestureHandler , State } from "react-native-gesture-handler"
import {usePanGestureHandler , useValue , timing , snapPoint} from "react-native-redash/lib/module/v1"
export default function VideoModal(props) {
const { video } = props;
const {gestureHandler , velocity , translation , state} = usePanGestureHandler()
const translateY = useValue(0)
const offsetY = useValue(0);
const snapPoints = snapPoint(translateY , velocity.y , [0 ,upperBound])
useCode(()=>block([
cond(
eq(state , State.ACTIVE),
[
set(translateY , add(offsetY , translation.y))
]
),
cond(
eq(state , State.END),
[
set(translateY , timing({from:translateY , to:snapPoints, duration:350})),
set(offsetY , translateY)
]
)
]))
return (
<>
<View
style={{
height: statusBarHeight,
backgroundColor: 'black',
}}
/>
<PanGestureHandler {...gestureHandler} >
<Animated.View
style={{
...shadow,
transform:[{translateY:translateY}],
zIndex:10
}}
>
<TouchableWithoutFeedback onPress={()=>alert('Working'}>
<View style={{ backgroundColor: 'white', width }}>
<View style={{ ...StyleSheet.absoluteFillObject }}>
<PlayerControls title={video.title} onPress={() => alert('abc')} />
</View>
<AnimatedVideo
source={video.video}
style={{ width, height:videoContent }}
resizeMode={Video.RESIZE_MODE_COVER}
shouldPlay
/>
</View>
</TouchableWithoutFeedback>
<Animated.View style={{ backgroundColor: 'white', width:contentWidth, height:contentHeight }}>
<Animated.View style={{opacity:contentOpacity}}>
**<VideoContent {...{ video }} />**
</Animated.View>
</Animated.View>
</Animated.View>
</PanGestureHandler>
</>
);
}
the scrollView is inside VideoContent
import {
View, StyleSheet, Text, Image, ScrollView,
} from 'react-native';
export default function VideoContent(props) {
const { video } = props;
return (
<ScrollView>
<View style={styles.upNext}>
<Text style={styles.upNextTitle}>Up next</Text>
{
videos.map(v => (
<View key={v.id} style={styles.thumbnail}>
<Image source={v.thumbnail} style={styles.thumbnailImage} />
<View style={styles.thumbnailContent}>
<Text numberOfLines={2} style={styles.thumbnailTitle}>{v.title}</Text>
<Text style={styles.thumbnailUsername}>{v.username}</Text>
</View>
</View>
))
}
</View>
</ScrollView>
);
}
Is there anyway I can tell pangesture to work only if the finger is placed on the top part of screen otherwise it should not work and i can scroll the content present inside the scrollview??

Hey i Know its bit late to reply to your answer but i also faced the similar issue, a bit of research unable to me find this trick
activeOffsetX={[-10, 10]}
in you pangestureHandler like this
<PanGestureHandler
onGestureEvent={onGestureEvent}
activeOffsetX={[-10, 10]}
onHandlerStateChange={evt =>handleStateChange(evt)}
>
source : https://www.gitmemory.com/issue/software-mansion/react-native-gesture-handler/1380/786721032
I hope it helps

Related

creating flatlist grid from api data [duplicate]

This question already has answers here:
CSS Grid - 2x2 grid always taking up the full width when possible
(1 answer)
How to set up a dynamic grid based on flex or grid
(1 answer)
Closed yesterday.
I want to create a dynamic grid in the flatlist, but it is not dynamic when data increases, my data is an array of object which has images and other information too.
I am using
columnWrapperStyle={{flexWrap: 'wrap', flex: 1 / 2}}
width: "100%",
height: height / 2.3,
I want to create something like this
if there is one image the grid should cover full width and if two images it should take half of width,
now the main issue I want to do something when there are three images the first height should be full and others will divide the height in half, same for the last one.
thanks.
You should use or get inspired by this package - https://www.npmjs.com/package/react-native-fb-image-grid
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import FbGrid from "react-native-fb-image-grid";
class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.inner_container}>
<FbGrid
images={[
"https://facebook.github.io/react-native/docs/assets/favicon.png",
]}
/>
</View>
<View style={styles.inner_container}>
<FbGrid
images={[
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
]}
/>
</View>
<View style={styles.inner_container}>
<FbGrid
images={[
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
]}
/>
</View>
<View style={styles.inner_container}>
<FbGrid
images={[
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
"https://facebook.github.io/react-native/docs/assets/favicon.png",
]}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#eee",
},
inner_container: {
width: "100%",
height: "20%",
},
});
export default App;
Live Demo - https://snack.expo.dev/#emmbyiringiro/c182c6

zIndex on component incorrectly stacking

// renderItem
<View>
<Image source={image} style={{position: 'absolute', zIndex: 40}} />
</View>
<View>
<ImageBackground source={image} style={{position: 'absolute', zIndex: 50}}/>
<Image source={image} style={{position: 'absolute', zIndex: 30}} />
<Image source={image} style={{position: 'absolute', zIndex: 20}} />
</View>
<FlatList renderItem={renderItem}/>
Idea is that the code above FlatList is fixed in place and image in renderItem can change position with scroll.
Expected Behavior
renderItem is stacked between zIndex of 50 and 30.
Current Behavior
renderItem is always at the back (before zIndex 20 eventhough renderItem has a zIndex of 40)
What I tried
For testing, I added another Image tag between 50 and 30 in the same file and that seems to be working. So I am guessing the issue only happens when componentizing it. I also tried messing with elevation since my target is Android but to no effect.
Any insight would be really helpful - thank you!
Working with zIndex sometimes happens this. For your case, I suggest you use the props ListHeaderComponent and stickyHeaderIndices to sticky the header when scrolling the list.
// Some code...
const Header = () => (
<View style={styles.header}>
<ImageBackground
style={styles.imageBackground}
source={require('./assets/image.png')}
>
<Text style={styles.headerTitle}>Fixed header</Text>
</ImageBackground>
</View>
);
// More code...
return (
<FlatList
ListHeaderComponent={Header}
stickyHeaderIndices={[0]}
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
)
I create a snack on Expo for you to see the complete example. Just scan the QR Code from your smartphone.

React-Native: Scroll view does not respect justify content?

When attempting to use ScrollView it appears to not respect justifyContent of its parent container.
import React from 'react';
import { Text, ScrollView, StyleSheet, TextStyle, View, ViewStyle } from 'react-native';
interface TODO_TextCard {
text: string,
}
export const TODO_TextCard: React.FunctionComponent<TODO_TextCard> = (props: TODO_TextCard) => {
return <View style={styles.viewStyle}>
<ScrollView>
<Text style={styles.quoteTextStyle}>{props.text}</Text>
</ScrollView>
</View>;
}
const styles = StyleSheet.create({
quoteTextStyle: {
fontSize: 30,
fontStyle: 'italic'
} as TextStyle,
viewStyle: {
flex: 1,
borderWidth: 2, borderColor: 'red',
justifyContent: 'center',
paddingHorizontal: 10
} as ViewStyle,
});
<TODO_TextCard text={'The mind adapts and converts to its own purposes the obstacle to our acting. The impediment to action advances action. What stands in the way becomes the way'}/>
Rendered as:
Now if I remove the and just render text such as
export const TODO_TextCard: React.FunctionComponent<TODO_TextCard> = (props: TODO_TextCard) => {
return <View style={styles.viewStyle}>
<Text style={styles.quoteTextStyle}>{props.text}</Text>
</View>;
}
The Text element does respect the justifyContent:center of the parent and renders as:
Is it possible for Scroll view to be centered?
The solution that I have in mind right now is to check the length of the text and conditionally render Scroll View something like:
/** This some text length would have to be different depending on the device screen, and
* even with different device screens would still not work all the time if the text
* can have new lines in it.*/
const SOME_TEXT_LENGTH = 300;
export const TODO_TextCard: React.FunctionComponent<TODO_TextCard> = (props: TODO_TextCard) => {
return <View style={styles.viewStyle}>
{props.text.length > SOME_TEXT_LENGTH ?
<ScrollView>
<Text style={styles.quoteTextStyle}>{props.text}</Text>
</ScrollView>
:
<Text style={styles.quoteTextStyle}>{props.text}</Text>
}
</View>;
}
Which very much is not ideal, due to different device screens as well as text potentially having new lines.
The ScrollView does in fact respect the justifyContent:center of its parent. The ScrollView is placed in the center of the outer View component. But here the ScrollView takes up the whole vertical screen, so it seems like it is not centered.
Try setting <ScrollView style={{backgroundColor: 'green'}}> to see what i mean.
Try applying viewStyle or a similar styling to the ScrollView itself. Make sure that you use the attribute contentContainerStyle instead of style. This snippet works for me.
<View style={styles.viewStyle}>
<ScrollView contentContainerStyle={{flex: 1, justifyContent: 'center'}}>
<Text style={styles.quoteTextStyle}>{props.text}</Text>
</ScrollView>
</View>
I also found this article. Maybe it will also help you out.
You need to give styling to ScrollView, for styling ScrollView you can use style or contentContainerStyle prop:
style defines the outer container of the ScrollView, e.g its height and relations to siblings elements
contentContainerStyle defines the inner container of it, e.g items alignments, padding, etc
In your case you need to give contentContainerStyle to position your items for eg:
return (
<View style={styles.viewStyle}>
{props.text.length > SOME_TEXT_LENGTH ?
<ScrollView
contentContainerStyle={{
flex: 1, //To take full screen height
justifyContent: 'center',
alignItems: 'center,
}}>
<Text style={styles.quoteTextStyle}>{props.text}</Text>
</ScrollView>
:
<Text style={styles.quoteTextStyle}>{props.text}</Text>
}
</View>
);

How do I conditonally add wrapper in loop React?

Actually my question is about React Native, but i think it's similar to React.
I'm tried to create dynamic menu with loop in array like this:
My question is: how do I conditionally add another <View/> while using map?
let say there's 4 value in array and I've tried to do something like this:
_renderMenu(){
let menu = this.state.menu.map((el, index)=>{
if(index==0){
return(
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
)
} else {
//Wrap the rest with <View style={MyStyle}>
return(
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
)
//</View>
}
})
return(
<View style={{flex:1, padding:5}}>
{menu}
</View>
)
}
My loop pattern something like this:
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
<View style={{flexDirection:'row', flex:1}}> //if index!==0 wrap the rest with this view
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
<View style={{flex:1, margin:5, borderRadius:5, backgroundColor:Color.amber_200}}/>
</View>
Is it possible to conditionally something like my case?
Based on the above comments, here's a solution for your problem:
First, you'll need to add lodash to the project to use the _.chunck method: https://lodash.com/docs/4.17.11#chunk
_renderMenu() {
const menuItem = (item, index) => (
<View key={`menu_item_${index}`} style={{ flex: 1, margin: 5, borderRadius: 5, backgroundColor: Color.amber_200 }}>{item}</View>
)
const splitArray = _.chunk(this.state.menu.slice(1), 3)
return (
<View style={{ flex: 1, padding: 5 }}>
{menuItem(this.state.menu[0], 0)}
{
splitArray.map((array, index) => {
return (
<View style={{ flexDirection: 'row', flex: 1 }}>
{array.map((element, elementIndex) => menuItem(element, (elementIndex * index) + elementIndex + 1))}
</View>
)
})
}
</View>
)
}
the splice will skip the specified number of elements, in our case, the first one. We will render the first menu item alone, then will render the other items.
Now for the key, i'm trying to keep it unique using the * + formula to generate them based on both indexes.

Aligning items with flexbox in react native

I'm trying to align Views in React Native, and I want that every View is 50% of the screen. Something like :
My state is :
this.state = {
companies: [
{logo_url: require("../../../img/company_logos/ald.png")},
{logo_url: require("../../../img/company_logos/arval.png")},
{logo_url: require("../../../img/company_logos/businesslease.png")},
{logo_url: require("../../../img/company_logos/dieterenusedcars.png")},
{logo_url: require("../../../img/company_logos/psa.png")},
{logo_url: require("../../../img/company_logos/remarketing_center.png")},
{logo_url: require("../../../img/company_logos/vdfin.png")}
]
}
And then in the render method :
render(){
return (
<View style={{flex: 1, flexDirection: 'row'}}>
{this.state.companies.map(company => {
return (
<View style={{width: Dimensions.get('window').width * 0.5, height: 150}}>
<Image source={company.logo_url} style={{width: Dimensions.get('window').width * 0.5, height: 100}}/>
</View>
);
})}
</View>
)
}
But I see only first two logos.
All items are probably shown in one row instead of going to second row if there is no more place on the screen.
Any advice?
In case somebody else has the same problem.
I solved it by adding flexWrap: "wrap" to the parent View
<View style={{flex: 1, flexDirection: 'row', flexWrap: "wrap"}}>

Resources