How do I display an animated gif in React Native? - react-native

How can I display an animated gif in react native. This is what I've tried.
<Image source={{uri: "loading"}} />
It works fine with a .png file but when I use a .gif file it's blank. I read somewhere to try renaming the .gif to a .png but that just displays one frame of the animated gif with no animation. Any ideas?

For RN < 0.60
By default the Gif images are not supported in android react native app.
You need to set use Fresco to display the gif images.
The code:
Edit your android/app/build.gradle file and add the following code:
dependencies: {
...
compile 'com.facebook.fresco:fresco:1.+'
// For animated GIF support
compile 'com.facebook.fresco:animated-gif:1.+'
// For WebP support, including animated WebP
compile 'com.facebook.fresco:animated-webp:1.+'
compile 'com.facebook.fresco:webpsupport:1.+'
}
then you need to bundle the app again, You can display the gif images in two ways like this.
1-> <Image
source={require('./../images/load.gif')}
style={{width: 100, height: 100 }}
/>
2-> <Image
source={{uri: 'http://www.clicktorelease.com/code/gif/1.gif'}}
style={{width: 100, height:100 }}
/>
For RN >= 0.60
implementation 'com.facebook.fresco:animated-gif:1.12.0' //instead of
implementation 'com.facebook.fresco:animated-gif:2.0.0' //use
I hope it is helpful to others,

You need to add the extension and require it this way :
<Image source={require('./path/to/image/loading.gif')} />
or
<Image source={{uri: 'http://www.urltogif/image.gif'}} />

For React Native 0.60 and higher
Open your android/app/build.gradle file and add following lines to first of dependencies section
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:animated-gif:2.0.0'
And then
cd android
gradlew clean
react-native run-android

For me on React native 0.65.1
The solution in react-native docs did not work
I had to use the latest version of Fresco:
implementation 'com.facebook.fresco:animated-gif:2.5.0'
Now GIF works on Android for me.
You can check Fresco latest from their website.

For Android You Need to Add Facebook's Fresco Library
React Native does not come with Gif support out of the box but you can add Facebook's Fresco library to add this support.
You should be able to simply add the following to your build.gradle file:
compile 'com.facebook.fresco:animated-gif:0.+'
Specific Version Compatibility
If you are having troubles or you want to use a static version (highly recommended), you can simply go to the following React Native documentation page and replace the 0.46 in the URL with the version of React Native you're running:
https://facebook.github.io/react-native/docs/0.46/image.html#gif-and-webp-support-on-android

if you want to use gif as background image than you can use
<ImageBackground
source={yourSourceFile}
>
-- your content ---
</ImageBackground>

To add gif and WebP in your project you need some optional modules. If the RN version is <=0.59 then add the following lines in your android/app/build.gradle file.
dependencies {
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
compile 'com.facebook.fresco:animated-base-support:1.10.0'
// For animated GIF support
compile 'com.facebook.fresco:animated-gif:1.10.0'
// For WebP support, including animated WebP
compile 'com.facebook.fresco:animated-webp:1.10.0'
compile 'com.facebook.fresco:webpsupport:1.10.0'
// For WebP support, without animations
compile 'com.facebook.fresco:webpsupport:1.10.0'
}
If the RN version is 0.60 and greater then add the following lines in android/app/build.gradle file
dependencies {
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
implementation 'com.facebook.fresco:animated-base-support:1.10.0'
// For animated GIF support
implementation 'com.facebook.fresco:animated-gif:1.12.0'
// For WebP support, including animated WebP
implementation 'com.facebook.fresco:animated-webp:1.10.0'
implementation 'com.facebook.fresco:webpsupport:1.10.0'
// For WebP support, without animations
implementation 'com.facebook.fresco:webpsupport:1.10.0'
}

DylanVann / react-native-fast-image is a nice alternative that supports GIFs for both Android (based on glide rather than fresco) and iOS (SDWebImage) with additional features that looks like:
const YourImage = () => (
<FastImage
style={{ width: 200, height: 200 }}
source={{
uri: 'https://unsplash.it/400/400?image=1',
headers: { Authorization: 'someAuthToken' },
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.contain}
/>
)

For react-native": "0.68.2" this version is working for me
implementation 'com.facebook.fresco:animated-gif:2.6.0'

import React,{useState} from 'react';
**step1 import from react narive You Can Use (Image) Or (ImageBackground) **
import { StyleSheet, Text, View ,ImageBackground} from 'react-native';
function LoadingUsers() {
return(<View style={styles.LoadingView}>
**Step 2 require inside source ImageBackground **
<ImageBackground source={require('../assets/stickman.gif')} style={styles.Gif}><Text>Loading..</Text></ImageBackground>
</View>)
}
**Step 3 Set Width ANd height **
const styles = StyleSheet.create({
LoadingView:{
flex:1,
},
Gif:{
flex:1,
width:"100%",
height:"100%",
justifyContent:"center",
alignItems:"center",
backgroundColor:'#000',
}
});
export default LoadingUsers ;

Open project/android/app then build.gradle file and find dependencies and add this line(dependency for .gif)
dependencies {
...
// For GIF image support
implementation 'com.facebook.fresco:animated-gif:2.5.0'
//or implementation 'com.facebook.fresco:animated-gif:2.6.0'
}
then in your react component you can pass any .gif or any gif url like so
const gifUrl = 'https://ipfs.io/ipfs/bafybeic75wkaqvblmte523qkzdoe437onrwl3xgvy5argmh6uhmrn7g2wi'
...
return (
<View>
<Image source={{uri: gifUrl}} style={{width: 200, height: 200}} />
</View>
)

For RN >= 0.66
Edit your android/app/build.gradle file and add the following code:
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:animated-gif:2.6.0'

In any case you need something else you could use also WebView for it
render(props) {
const width = 220;
const height = 135;
const borderRadius = 15;
const uri = 'https://c.tenor.com/0wj4ApfUlWUAAAAM/whatever-bank-stare.gif';
// const uri = 'https://c.tenor.com/YwsCGem_MmQAAAAC/parks-and-rec-amy-poehler.gif',;
// const uri = 'https://media.tenor.com/images/1c39f2d94b02d8c9366de265d0fba8a0/tenor.gif';
return (
<View
style={{
width,
height,
borderRadius: 15,
overflow: 'hidden',
}}>
<WebView
source={{
uri
}}
style={{
flex: 1,
width,
height,
borderRadius,
}}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
injectedJavaScript={`
document.body.style.width = "${width}px";
document.body.style.height = "${height}px";
document.body.style.backgroundColor = "${'#fff'}";
document.body.style.overflow = "hidden";
const img = document.querySelector("img");
img.style.position = "absolute";
img.style.top = 0;
img.style.left = 0;
img.style.margin = "auto";
img.style[img.offsetWidth > img.offsetHeight ? 'width' : 'height'] = "100%";
img.style.borderRadius = "${borderRadius}px";
`}
/>
</View>
);
}
It will preserves the gif aspect ratio
Also, I did the same with the <Image />. For anyone need it:
function ChatImageGIF(props) {
const maxWidth = 220;
const maxHeight = 135;
const [width, setWidth] = useState(maxWidth);
const [height, setHeight] = useState(maxHeight);
const borderRadius = 15;
Image.getSize(props.currentMessage.video, (w, h) => {
const minWidth = Math.min(maxWidth, w);
const minHeight = Math.min(maxHeight, h);
const aspectRatio = (w < h ? w : h) / (w < h ? h : w);
setWidth(w < h ? minHeight * aspectRatio : minWidth);
setHeight(w > h ? minWidth * aspectRatio : minHeight);
});
return (
<View
style={{
width,
height,
borderRadius: 15,
overflow: 'hidden',
}}>
<Image
source={{
uri: props.currentMessage.video,
}}
style={{
width,
height,
borderRadius,
resizeMode: 'contain',
}}
/>
</View>
);
}

Related

React Native - set image as background using ImageBackground

I'm trying to render an Image as background, but is not working, I don't know why. Here is my code.
<ImageBackground
source={require('../assets/images/logos/AFC.svg')}
resizeMode="cover"
style={styles.style}>
</ImageBackground>
React Native does not directly support using SVG format images. In order to use SVG Images you must use 3rd party libraries. I suggest using react-native-svg. Which is a great library and here is a tutorial you can use to set it up.
Your use case is to set it as a background image. It would be better to use png or jpg formats for use with Image Background component in react native. If you only have svg format of the image, then you can set it in View and control the view
You should use react-native-svg to display SVGs.
if you want a svg in background you can do this example below
import { View, Text, Dimensions } from 'react-native'
import React from 'react'
import AFCIcon from '../assets/images/logos/AFC.svg'
const SCREEN = Dimensions.get("screen");
const App = () => {
return (
<View style={{ flex: 1 }}>
<View style={{
position: "absolute",
width: "100%",
height: "100%",
bottom: 0,
zIndex: -1,
}}>
<AFCIcon width={SCREEN.width} height={SCREEN.height} />
</View>
{/* your code here */}
</View>
)
}
export default App

Reanimated: animated view doesn't react to value changes

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';

How can i remove extra white space in expo BarCodeScanner

iOS is working fine BarCodeScanner take full screen but when i use android there is extra white space.
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this._handleBarCodeScanned}
style={[StyleSheet.absoluteFill, { flex: 1 }]}
/>
I have also checked by giving a different style like but no luck
style={{
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
}}
This seems to be an issue in recent versions of expo-barcode-scanner. One possible workaround is to explicitly set the dimensions of the BarCodeScanner to the dimensions of the screen:
import { Dimensions } from 'react-native';
<BarCodeScanner style={{
width: Dimensions.get('screen').width,
height: Dimensions.get('screen').height,
}} />
Note that setting it to the dimensions of the window, like you tried, does not work.
FrederikVds's answer not worked for me. So I have changed the expo camera which has the barcode scanner functionality too. You can do it like following:
import { Camera } from 'expo-camera'
<Camera
onBarCodeScanned={scanned ? undefined : this._handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
Optionally you can use ratio='16:9'.
If you need to use expo libraries in react-native cli, you should follow these setups
Here is the issue discussion: https://github.com/expo/expo/issues/5212
import { BarCodeScanner, BarCodeScannerResult } from
'expo-barcode-scanner'
const width = Dimensions.get('window').width;
const height = Dimensions.get('window').height;
export default function XYZ() {
return(
<BarCodeScanner
onBarCodeScanned={scanned ? undefined :
handleBarCodeScanned}
style={{ width: height - 188, height: height,
alignSelf: "center" }}
/>
)
}

SegmentedControlIOS for android in react-native

I am confused with the usage of SegmentedControlIOS in react-native, i check it in IOS simulator it works, But when i check it in android it throws an error as below
SegmentedControlIOS is not supported on this platform
here is my code:
<View >
<SegmentedControlIOS
tintColor="#D7D7D5"
style={styles.SegmentedControlIOS}
values={this.state.values}
selectedIndex={this.state.selectedIndex}
onChange={this._onChange}
onValueChange={(val) =>{
this.setState({
value:val
})
}}/>
</View>
Can anyone give me suggestions on how to use SegmentedControlIOS for both android and IOS, Any help in this regard is much appreciated.
SegmentedControl is a built in native component on iOS. However, there is no direct equivalent on Android which is why the react native component name ends with IOS and isn't support on Android. There is no obvious way make the built in component work on Android.
That leaves you with two options:
Use or create your own version using standard components. This library has a good approximation of a segmented control that would work on both operating systems.
Use two separate components on iOS and Android which can be done automatically by creating two files named:componentName.android.js and componentName.ios.js (See here for more information using different code for each platform).
The iOS specific code could use the iOS segmented control and the Android version could use something like https://github.com/zzyyppqq/react-native-segmented-android or a custom implementation.
See react-native-segmented-control-tab
for similar usage between both platform:
__
-
or
see ButtonGroup from react-native-elements
https://react-native-training.github.io/react-native-elements/docs/button_group.html
Very simple component, 100% compatible with IOS version.
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var { Component, View, Text, TouchableOpacity } = ReactNative;
var SimpleSegmentedControl = React.createClass({
getInitialState: function () {
return {
values: this.props.values || [],
selectedIndex: this.props.selectedIndex || 0,
style: this.props.style || {},
onChange: this.props.onChange
};
},
componentWillReceiveProps: function (props) {
this.setState(props);
},
onPress: function (selectedIndex) {
if (typeof this.state.onChange === 'function') {
return this.state.onChange(selectedIndex);
}
},
render: function () {
return (
<View style={[{flexDirection: 'row', borderWidth: 1, borderColor: '#007AFF', borderRadius: 5}, this.state.style]}>
{this.state.values.map(function (value, position, values) {
return (
<TouchableOpacity key={position} onPress={()=>this.onPress(position)} style={{flex: 1}}>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center', padding: 5,
backgroundColor: this.state.selectedIndex == position ? '#007AFF' : 'transparent',
borderRightWidth: position < values.length - 1 ? 1 : 0, borderRightColor: '#007AFF'}}>
<Text style={{fontSize: 13, color: this.state.selectedIndex == position ? 'white' : '#007AFF'}}>{value}</Text>
</View>
</TouchableOpacity>
);
}.bind(this))}
</View>
);
}
});
module.exports = SimpleSegmentedControl;
A good equivalent for SegmentedControlIOS should be Swipe Views with tab views:
https://developer.android.com/training/implementing-navigation/lateral.html
To use it in React Native android version you can use this library: https://github.com/skv-headless/react-native-scrollable-tab-view
It's strange that React Native's team not purpose this native component built in framework

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>