React Native ScrollView with single child component - reactjs

I want to have a fullscreen ScrollView which contains all of the contents of my Home screen in my app. The issue I'm facing is that it works as expected when I have two child components for the ScrollView (I added a Text component just to try it), but not if I just have the single content View that I want.
The contents of <View style={style.content}> are taller than the screen size, so it should scroll. But when I remove <Text style={{margin: 60, marginTop: 800}}>Here is some text</Text> the entire screen goes white.
Here's what I've heard about ScrollView:
It needs a limited height (but I should have that since all of the content is currently static (no dynamic data) and all of my components there have a fixed height)
You need to use {flex: 1} on the components inside the ScrollView (I have this style set on my Container component)
Here is my Home component:
import React, { Component } from "react";
import {
StatusBar, StyleSheet, View, ScrollView, Text
} from "react-native";
import { LinearGradient } from "expo";
import MaterialCommunityIcon from "react-native-vector-icons/MaterialCommunityIcons"
import Color from "color";
import globalStyles from "../config/globalStyles";
import Container from "../components/Container/Container";
import Header from "../components/Header/Header";
import FullWidthImage from "../components/Images/FullWidthImage";
import MainFeedbackText from "../components/Text/MainFeedbackText";
import MainStatistics from "../components/Statistics/MainStatistics";
import Button from "../components/Buttons/Button"
export default class Home extends Component {
render() {
return (
<ScrollView style={{borderColor:"red", borderWidth: 2}}>
<Container>
<View style={style.content}>
<MainFeedbackText/>
<Button
text="START NEW WORKOUT"
textStyle={{fontSize: 24}}
buttonStyle={{
backgroundColor: globalStyles.colors.green,
paddingHorizontal: 20,
}}
icon={<MaterialCommunityIcon style={style.settings} name="dumbbell" color="white" size={30}/>}
/>
<Button
text="My workouts"
textStyle={{fontSize: 24, paddingRight: 40}}
buttonStyle={{
backgroundColor: Color("black").fade(0.8),
paddingHorizontal: 20,
margin: 20
}}
icon={<MaterialCommunityIcon style={style.settings} name="format-list-bulleted" color="white" size={20}/>}
/>
<MainStatistics/>
</View>
{/**TODO: ScrollView breaks when removing this line*/}
<Text style={{margin: 60, marginTop: 800}}>Here is some text</Text>
<StatusBar translucent={false}/>
<LinearGradient colors={["transparent", globalStyles.colors.dark]} style={style.gradient}>
<View style={style.image}>
<FullWidthImage requireSource={require("./lifting.jpg")}/>
</View>
</LinearGradient>
<Header/>
</Container>
</ScrollView>
);
}
}
const style = StyleSheet.create({
image: {
opacity: 0.3,
zIndex: -1,
},
gradient: {
position: "absolute",
top: 0,
zIndex: -1
},
content: {
flex: 1,
position: "absolute",
top: 80,
height: 4000,
}
});

Set contentContainerStyle={{ flex: 1 }} for ScrollView to become fullscreen.

Related

React Native Tab Navigator: empty space at bottom of tab bar

I'm using react-navigation#4.0.10 and react-native#0.63.5 in my React Native app, and when I use createBottomTabNavigator, there's a gap underneath the tab labels on iPhone 11 Pro. It does not do the same on iPhone 12. My code for the TabNavigatorOptions is as follows
const TabNavigatorOptions = {
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {
activeTintColor: TabColors.activeColor,
inactiveTintColor: TabColors.labelColor,
bottomNavigationOptions: {
labelColor: TabColors.labelColor,
rippleColor: "white",
shifting: false,
activeLabelColor: TabColors.activeColor,
backgroundColor: TabColors.backgroundColor
},
style: {
height: 56, elevation: 8, position: 'absolute', left: 0, bottom: 0, right: 0
}
}
}
I've tried adding paddingBottom: 0 to the style object, but it makes no difference.
Does anyone know how I can approach this?
UPDATE:
If I add a red background in tabBarOptions -> style, with SafeAreaView I get this:
and if I remove SafeAreaView I get this
The only solution I could find for now is to remove the bottom inset from SafeAreaView. It's not a good solution in my opinion but at least it works:
import * as React from "react";
import { SafeAreaView } from "react-navigation";
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
forceInset={{ top: "always", bottom: "never" }}
>
<AppNavigator />
</SafeAreaView>
)
}
And if you are using react-native-safe-area-context:
import * as React from "react";
import { SafeAreaView } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
edges={["right", "top", "left"]}
>
<AppNavigator />
</SafeAreaView>
);
}

How to play a segment of a Lottie Animation (.json file ) in React Native

I want to play only a segment of the animation in react native using lottie view
the length is about 5 seconds while in speed={1} I wanna play only the first 3 seconds and then go to the next screen
the code is down below
LogoScreen.js :
import React from 'react';
import { StyleSheet, View, Image, TextInput, StatusBar, Text } from "react-native";
import Icons from 'react-native-vector-icons/Ionicons';
import LottieView from "lottie-react-native";
export default class ChatScreen extends React.Component {
onAnimationFinish = () => {
this.props.navigation.navigate("Login")
}
render () {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" backgroundColor="#161f3d" />
<View>
<LottieView
source={require('../assets/animations/lottie/MotionCorpse-Jrcanest.json')}
style={{ justifyContent: "center", alignSelf: "center", height: "100%", width: "100%" }}
autoPlay
loop={false}
speed={1}
onAnimationFinish={this.onAnimationFinish}
/>
</View>
</View>
)
}
Well you can do it by several ways, one of the way would be like below.
You can use ref to play it manually and then after 3 seconds just redirect to next screen.
import React from 'react';
import { StyleSheet, View, Image, TextInput, StatusBar, Text } from "react-native";
import Icons from 'react-native-vector-icons/Ionicons';
import LottieView from "lottie-react-native";
export default class ChatScreen extends React.Component {
componentDidMount() {
this.anim.play();
setTimeout(() => {
this.props.navigation.navigate("Login")
}, 3000);
}
render() {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" backgroundColor="#161f3d" />
<View>
<LottieView
source={require('../assets/animations/lottie/MotionCorpse-Jrcanest.json')}
style={{ justifyContent: "center", alignSelf: "center", height: "100%", width: "100%" }}
autoPlay={false}
loop={false}
speed={1}
ref={animation => { this.anim = animation; }}
/>
</View>
</View>
)
}
}
Another way is if you know exact frame numbers then you can play animation till that frame which completes at 3 seconds. It is already mentioned in Lottie documentation.
this.animation.play(30, 120);

How to design fieldset legend using react-native

In HTML we can use fieldset-legend like this:
Code:
<fieldset>
<legend>Heading</legend>
</fieldset>
O/P:
How can I design the same things using react-native? Here I got a technique where it created text with a straight line, but I want to design it like fieldset-legend. How can I do that?
I got a technique to create this fieldset-legend without any library/package. Here is my JSX code:
JSX view:
<View style={styles.fieldSet}>
<Text style={styles.legend}>Heading</Text>
<Text>Some Text or control</Text>
</View>
JSX style:
const styles = StyleSheet.create({
fieldSet:{
margin: 10,
paddingHorizontal: 10,
paddingBottom: 10,
borderRadius: 5,
borderWidth: 1,
alignItems: 'center',
borderColor: '#000'
},
legend:{
position: 'absolute',
top: -10,
left: 10,
fontWeight: 'bold',
backgroundColor: '#FFFFFF'
}
});
You could you Readymade package - react-native-fieldset
Its very easy to work with it.
You could directly do-
import FieldSet from 'react-native-fieldset';
import { View, Text } from 'react-native';
//...
return (
<View>
<FieldSet label="Fieldset label">
<Text>Field Set Body</Text>
</FieldSet>
</View>
);
//...
This FieldSet is not working
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, TextInput } from 'react-native';
import FieldSet from 'react-native-fieldset';
import LinearGradient from 'react-native-linear-gradient'
// create a component
export default function MyComponent() {
return (
<View>
<FieldSet label="Fieldset label" label2='try'>
<TextInput placeholder='House Name/ Number'/>
</FieldSet>
</View>
);
};

Multiple onPress event on a single image component in React Native

I have this picture, and I want to have multiple onPress event for this image.
Example if I touch the head part it will call the function pressHead() and if I touch the chest part it will call the function pressChest().
So far I have tried plotting checkboxes on each part.
import React, { Component } from 'react';
import { View, Image, Alert } from 'react-native';
import { CheckBox } from 'react-native-elements';
export default class Screen extends Component {
pressHead() {
this.setState({checked1: !this.state.checked1})
Alert.alert('Pressed Head', '');
}
pressChest() {
this.setState({checked2: !this.state.checked2})
Alert.alert('Pressed Chest', '');
}
render() {
return (
<View style={{width: 200}}>
<Image
style={{width: 200, resizeMode: 'contain'}}
source={require('../../assets/images/body-diagram.png')}
/>
<CheckBox
containerStyle={{position: 'absolute', top: 22, right: 75, padding: 0}}
checkedIcon='dot-circle-o'
uncheckedIcon='circle-o'
checkedColor='#ff0000'
checked={this.state.checked1}
onPress={() => this.pressHead()}
/>
<CheckBox
containerStyle={{position: 'absolute', top: 70, right: 75, padding: 0}}
checkedIcon='dot-circle-o'
uncheckedIcon='circle-o'
checkedColor='#ff0000'
checked={this.state.checked1}
onPress={() => this.pressChest()}
/>
</View>
);
}
}
This does work. But if I try to use it on a larger device, the position absolute becomes not accurate enough.
Give the constant height for the image and overlay the checkboxes as same above respect to image. Since height is also made constant you don't find any position issues of the checkbox in any screen.
Example :
<View style={{width: 200, height: 600}}>
<Image
style={{width: 200, height: 600 ,resizeMode: 'contain'}}
source={require('../../assets/images/body-diagram.png')}
/>
You can do it by changing your image to <ImageBackground .. /> component after importing it from react-native.
After doing that you can place Touchable things inside of ImageBackground.
Example:
<ImageBackground source={require('../../assets/images/body-diagram.png')} style={{width: 200, height: 600, flexDirection: 'column'}}
<TouchableOpacity onPress={() => alert('first pressed')}>
<Text>First Area</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => alert('second pressed')}>
<Text>Second Area</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => alert('third pressed')}>
<Text>Third Area</Text>
</TouchableOpacity>
<ImageBackground/>

Simple React Native Image Game is Lagging.How do i optimize?

I am trying to implement a simple img tapping game with react native for learning purposes.I am using this librarys https://github.com/bberak/react-native-game-engine game loop component as a game loop.
What I am trying to do is render some imgs with random locations on screen with small radius then increase it up to a value, after decrease it and finally remove the img from screen (or when tapped) - just like in this game: http://mouseaccuracy.com/
import React, { Component } from "react";
import { AppRegistry, StyleSheet, Dimensions, View,ToastAndroid,TouchableOpacity,Image,Text } from "react-native";
const { width: WIDTH, height: HEIGHT } = Dimensions.get("window");
import { GameLoop } from "react-native-game-engine";
import { Fonts } from '../utils/Fonts';
import FastImage from 'react-native-fast-image'
import * as Progress from 'react-native-progress';
import SwordImage from "../assets/tapfight/sword.png";
import ShieldImage from "../assets/tapfight/shield.png";
this.renderSwordOrCircle(circle)
)
})
)
}
}
renderHealth = () =>{
return(
<View>
<View style={{ alignItems:'center' ,position: "absolute", top: 0, left: 0}}>
<Text style={{fontFamily:Fonts.MainFont,color:'white',fontSize:25}}>{this.playerHealth}</Text>
<Progress.Bar color='red' progress={this.playerHealth/100} width={100} height={18} />
</View>
<View style={{ alignItems:'center',position: "absolute", top: 0, right: 0}}>
<Text style={{fontFamily:Fonts.MainFont,color:'white',fontSize:25}}>{this.enemyHealth}</Text>
<Progress.Bar color='red' progress={this.enemyHealth/100} width={100} height={18} />
<FastImage resizeMode="contain" style={{width:100,height:100}} source={require('../assets/tapfight/guard_2_head.png')}></FastImage>
</View>
</View>
)
}
render() {
if(this.state.fight==true){
return (
<View style={{flex:1,backgroundColor:'transparent'}} >
<GameLoop style={{flex:1}} onUpdate={this.updateCircle}>
{this.renderHealth()}
{this.renderCircles()}
</GameLoop>
</View>
);
}else{
return(
null
)
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
What i am doing is i am generating images every 600 ms on screen and player tries to tap them.The problem is when there is more than 2 images on screen fps drops significantly.Tested in android actual device.I am updating state once at the end of updateCircle function.
Game loop is updateCircle component
Your problem is that you're trying to animate a component that is not made for being animated.
I dont know well the FastImage component, but I understand that it purpose is to pre-load images, not animate.
You shoud use Animated.Image for the image, and Animated.timing for managing the animations of Animated.Image

Resources