Reanimated: animated view doesn't react to value changes - react-native

What you will see below is a minimised version of a bigger draggable solution I'm trying to implement—and it requires to have an animated view that would react to changes in animated style. This example isn't containing any gesture code since it's irrelevant here.
I have two rectangles: first is a Button that changes offset value randomly; second one is AnimatedRectangle that is supposed to be changing position each time the Button is pressed. That's it.
Expected result: AnimatedRectangle moving when Button is pressed.
Actual result: nothing moves.
FYI: share values, as well as animated style are changing, but the animated view doesn't seem to react to these changes.
Weird part is that when I was trying the same code in another project it worked in some files but not in others, although the styling and the way these different components were defined are the same. I have no idea why it happens.
Steps to reproduce:
Click on the Button
Observe the blue rectangle
Repo link: https://github.com/tumanov-alex/reanimated-not-working
import React from 'react';
import {View, TouchableOpacity, Animated, Text} from 'react-native';
import {useAnimatedStyle, useSharedValue} from 'react-native-reanimated';
const App = () => {
const offset = useSharedValue({x: 0, y: 0});
const animatedStyle = useAnimatedStyle(() => ({
transform: [{translateX: offset.value.x}, {translateY: offset.value.y}],
}));
const Button = () => (
<TouchableOpacity
onPress={() => {
offset.value = {
x: Math.random() * 100,
y: Math.random() * 100,
};
}}>
<View style={{width: 500, height: 500, backgroundColor: 'grey'}} />
</TouchableOpacity>
);
const AnimatedRectangle = () => (
<Animated.View
style={[
animatedStyle,
{
width: 100,
height: 100,
backgroundColor: 'blue',
},
]}>
<Text style={{color: 'white'}}>Why I'm not moving?</Text>
</Animated.View>
);
return (
<View style={{flex: 1}}>
<Button />
<AnimatedRectangle />
</View>
);
};
export default App;
Reanimated version: 2.11.0
React Native version: 0.70.3
Platforms: Android, iOS
Device: iOS simulator

I think that you have to import Animated directly from react-native-reanimated library and not from react-native:
Like this.
import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated';

Related

Issue with images styling inside a FlatList - React Native

I'm trying to design an application screen (I used React Navigation) where several images are displayed vertically inside a FlatList. Each image takes up around 90% of the screen, is centered and has some border-radius, to see the other images, the user has to scroll down.
All these things I was able to do myself, the only problem is, when I scroll down the first image, there is a huge empty gap at the top before showing image 2, and when I scroll down again to see image 3, the bottom bit of image 2 is still showing in the upper part of the screen, separated by another huge gap and the bottom of image 3 is cut out of the screen.
What I've been trying to do is to show exactly one full image at a time, and I don't know whether there's something wrong with the scrolling itself, or the styling I applied to the components.
Here's the code:
import {View, FlatList, Image, Dimensions} from 'react-native';
import {pictures} from './pictures.js';
const ImageScreen = () => {
const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('screen').height;
function renderImages(pictures){
return(
<View style={{ width: deviceWidth, height: deviceHeight, alignItems:'center'}}>
<Image source={{uri: pictures.item.imageId}}
style={{height:'90%',width:'90%', borderRadius:22 }}/>
</View>
);}
return(
<FlatList data={pictures} keyExtractor={(item)=> item.id} renderItem={renderImages}
pagingEnabled={true} />
);
};
export default SightOverview;
please find below code to display list:
import * as React from 'react';
import { View, FlatList, Image, Dimensions } from 'react-native';
const App = () => {
const pictures = [{
imageId: 'https://picsum.photos/id/1/200/300'
}]
const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('screen').height;
function renderImages({ item }) {
console.log(item)
return (
<View style={{ flex: 1, width: deviceWidth, height: deviceHeight, alignItems: 'center' }}>
<Image source={{ uri: item.imageId }}
style={{ width: deviceWidth * 0.8, height: deviceHeight * 0.5, borderRadius: 22 }} />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList data={pictures} keyExtractor={(item) => item.id} renderItem={renderImages}
pagingEnabled={true} />
</View>
);
};
export default App;

Change the backgroundcolor of Touchable / Pressable Item after pressing

I'm currently working on an app prototype with react native. There's a lot out there on how to change the color of a component, here Touchable or Pressable, when pressing it (=> onPress).
But how do i change the backgroundcolor of such a component permanently after clicking – with onPressOut?.
Example:
simple "Click me" component that has a green background by default. If clicked once, it should change to a red background. Clicked once again, it should go back to green (and so on).
Can you help me with this?
You need to control it using the state of component.
I did a live demo for you:
https://codesandbox.io/s/silent-sea-5331l?file=/src/App.js
import React, { useState } from "react";
import { View, TouchableOpacity } from "react-native";
const App = props => {
const [selected, setSelected] = useState(false);
return (
<View style={{ width: "30%", alignItems: "center", marginTop: 20 }}>
<TouchableOpacity
onPress={() => setSelected(!selected)}
style={{ backgroundColor: selected ? "red" : "transparent" }}
>
Press me
</TouchableOpacity>
</View>
);
};
export default App;

How to implement modal swipe to down like instagram?

I'm currently working on an React Native project. I need to implement a feature similar to "Swipe Down To close Modal" on Instagram
I believe that you're using #react-native-community/react-native-modal package. In the library documents, you can see that there is a property called swipeDirection which is either a string or an array that can be one or many of the following options; 'up', 'down', 'left' or 'right.
You can also set the threshold required swipe action to be completed by using swipeThreshold property. The default is 100 according to the library documents.
Here's an example of the modal;
import React from 'react';
import {View, Text} from 'react-native;
import Modal from 'react-native-modal';
const ModalComponent = props => {
const [isVisible, setIsVisible] = useState(true);
return (
<Modal isVisible={isVisible}
swipeDirection="down"
//swipeDirection={["up", "down", "left", "right"]}
onSwipeComplete={(e) => { setIsVisible(false); })
style={{ justifyContent: 'flex-end', margin: 0, }} >
<View style={{ backgroundColor: 'steelblue' }}>
<Text>Hello Modal</Text>
</View>
</Modal>
);
};
export {ModalComponent};

Why is my image (and child components) not showing with ImageBackground on React Native?

I am having an issue with the image just showing up on the application. Through search results, I mostly see the solution being that that the height and width was required and missing, but I have already implemented with no results.
I have looked at the routes multiple times and don't see any issues on that end.
Lastly, I thought it could be something to do with the image. I have altered the size and it worked with the <Image /> tag. However, I need it to be a background image.
Current Code
import React from 'react'
import { View, Text, StyleSheet, ImageBackground } from 'react-native';
import { MaterialCommunityIcons } from '#expo/vector-icons'
export default function TitleScreen () {
return (
<View>
<ImageBackground
source={require('../assets/images/title_background.png')}
style={styles.backgroundImage}
imageStyle={{resizeMode: 'cover'}}
>
<Text>testing</Text>
{/* <MaterialCommunityIcons
name={'cards-outline'}
size={100}
/> */}
</ImageBackground>
</View>
)
}
const styles = StyleSheet.create({
backgroundImage: {
flex: 1,
width: '100%',
height: '100%',
}
});
Folder Layout
assets
-- images
--- title_background.png
components
-- TitleScreen.js
Answered! The answer was to add flex: 1 to the parent view container - for those running into the same problem.

React Native <ScrollView> persistent scrollbar

After perusing the React Native Documentation I couldn't seem to find out how to make a <ScrollView> have a persistent scrollbar that doesn't fade out. How would I achieve that?
iOS
The underlying iOS native component, UIScrollView (technically, RCTEnhancedScrollView), doesn't support keeping the scroll indicators visible. For this reason, the React Native wrapper around it won't either.
There is a hack to get this working with the native component (see this answer for one approach). To accomplish this in React Native, you'd need to implement this hack on the native side, and then either create your own Native Module or fork React Native and modify their ScrollView component.
That said, the iOS Scroll View interface guidelines discourage this, so you may want to leave the indicators' behavior alone.
Android
A few approaches:
set <item name="android:overScrollMode">always</item>,
set android:fadeScrollbars="false" in XML, or
set ScrollView.setScrollbarFadingEnabled(false) in Java (e.g. in your custom native bridge code)
This is similarly discouraged as nonstandard UI unless you have a strong reason for it.
Adding answer since none of the above worked for me.
Android now has the persistentScrollbar props.
iOS does not support this. So I created a JS solution that can be used as follows:
<SBScrollView persistentScrollbar={true}>...</SBScrollView>
Basically, this functional component will use persistentScrollbar when on Android, while add a bar when we are on iOS. It is not smooth for now, but it is functional.
// #flow
import React, {useState} from 'react';
import {Platform, View, ScrollView} from 'react-native';
type Props = {|
persistentScrollbar?: boolean,
children?: React$Node,
|} & View.propTypes;
export default function SBScrollView({
persistentScrollbar = false,
children,
...other
}: Props) {
const [nativeEvent, setNativeEvent] = useState();
if (Platform.OS === 'android' || !persistentScrollbar) {
// Abdroid supports the persistentScrollbar
return (
<ScrollView persistentScrollbar={persistentScrollbar} {...other}>
{children}
</ScrollView>
);
}
const top = nativeEvent
? nativeEvent.contentOffset.y +
(nativeEvent.contentOffset.y / nativeEvent.contentSize.height) *
nativeEvent.layoutMeasurement.height
: 0;
// iOS does not support persistentScrollbar, so
// lets simulate it with a view.
return (
<ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={event => setNativeEvent(event.nativeEvent)}
{...other}>
{children}
<View
style={{
position: 'absolute',
top,
right: 4,
height: 200,
width: 4,
borderRadius: 20,
backgroundColor: 'gray',
}}
/>
</ScrollView>
);
}
I hope this can help others.
I was looking for a solution but I didn't find nothing, then I created a solution, I hope can help you with it.
I created a view View with height and width and put it over my scrollview, after that I used the Props of scrollview like onMomentumScrollBegin, onMomentumScrollEnd, onContentSizeChange and onScroll
after that I make a condition with a boolean variable, if this variable is false, the View is visible, if is false the View is hide, How do I active this variable? with the Prop onMomentumScrollBegin that detect when you use the scrollView and the same way to set the variable in false with onMomentumScrollEnd that detects when the scroll ends.
The Prop onContentSizeChange allows me to get the height and width of my scrollview, this values I used to calculate where would be set the scrollbar/scrollIndicator
and finally with the Prop onScroll I get the position.
the example:
<ScrollView
onMomentumScrollBegin={() => {this.setvarScrollState()}}
onMomentumScrollEnd={() => {this.setvarScrollStateRev()}}
scrollEventThrottle={5}
onContentSizeChange={(w, h) => this.state.hScroll = h}
showsVerticalScrollIndicator={false}
onScroll={event => { this.state.wScroll = event.nativeEvent.contentOffset.y }}
style={{ marginVertical: 15, marginHorizontal:15, width: this.state.measurements3.width}}>
{
Mydata.map((value, index) => {
return <TouchableOpacity>
<Text>{ value.MyDataItem }</Text>
</TouchableOpacity>
})
}
the functions:
setvarScrollState() {
this.setState({VarScroll: true});
}
setvarScrollStateRev() {
this.setState({VarScroll: false});
}
and the variable
this.state = {VarScroll: false}
Then my condition is
!this.state.VarScroll ?
<View
style = {{
marginTop: 200*(this.state.wScroll / this.state.hScroll),
marginLeft:338.5,
height: 35,
width: 2,
backgroundColor: 'grey',
position:'absolute'
}}
/>
: null
Why 200? because is the maximum value that my marginTop can set
Check the picture
Final note:
the scrollView have to be inside a View with the another View (scrollbar)
something like this
<View>
{/*---- ScrollBar and conditions----*/}
<View>
<View>
<ScrollView>
</ScrollView>
</View>