How to export View to image data in React Native - react-native

I am trying to export the rendered graphic of a View in React Native. For example, my view is like:
<View>
<Image/> // Some image
<Rectangle /> // Some little
<Circle /> // Some circle
</View>
How do I extract the rendered graphic from the container View into whatever image data format?

see https://stackoverflow.com/a/31936516/528842
Subclass RCTView and add export method:
MyCoolView.m:
- (NSData *)export
{
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return UIImagePNGRepresentation(image);
}
Expose the export method in the native view manager:
The key is to pass in the reactTag which is the reference to the native component.
MyCoolViewManager.m:
RCT_EXPORT_METHOD(export:(NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) {
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
MyCoolView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[MyCoolView class]]) {
RCTLogMustFix(#"Invalid view returned from registry, expecting MyCoolView, got: %#", view);
}
NSData * imageData = [view export];
callback(#[[NSNull null], [imageData base64EncodedStringWithOptions:0]]);
}];
}
Expose export method from React component:
MyCoolView.js:
var React = require('react-native');
var NativeModules = require('NativeModules');
var MyCoolViewManager = NativeModules.MyCoolViewManager;
var findNodeHandle = require('findNodeHandle');
class MyCoolView extends React.Component{
// ...
export() {
return new Promise((resolve, reject) => {
MyCoolViewManager.export(
findNodeHandle(this),
(error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
// now we've got the base64 encoded data of the exported image
}
}
);
});
}
}
Invoke the export method:
The component look like this:
<MyCoolView ref='myCoolView'>
<Image />
<Rectangle />
<Circle />
</View>
</MyCoolView>
In some function:
this.refs.myCoolView.export()
.then(base64data => {
console.log(base64data);
}, error => {
console.error(error);
});

Related

Use value from AsyncStorage for VideoThumbnails.getThumbnailAsync

I'm trying to use the expo-video-thumbnails package to generate a thumbnail from a video uri. The video uri is retrieved from async-storage. I get the following error
Argument of an incompatible class: class java.util.HashMap cannot be
passed as an argument to parameter expecting class java.lang.String.
If I use a static video uri, it works fine. I can't seem to get this to work with the value from AsyncStorage. Here's my code:
import React, { useState, useEffect } from 'react';
import { View, Image } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as VideoThumbnails from 'expo-video-thumbnails';
export default function ThumbnailsScreen() {
// video uri
const [videoUri, setVideoUri] = React.useState({});
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#lastRecordedVideo')
if(value !== null) {
setVideoUri(value)
}
} catch(e) {
console.log(e);
}
}
getData();
// thumbnails
const [image, setImage] = useState(null);
const generateThumbnail = async () => {
try {
const { uri } = await VideoThumbnails.getThumbnailAsync(
videoUri,
{
time: 1000,
}
);
setImage(uri);
} catch (e) {
console.warn(e);
}
};
useEffect(() => { generateThumbnail(); }, [])
return (
<View>
{image && <Image source={{ uri: image }} /> }
</View>
)
}

How to update theme when device orientation changes

I am trying to implement orientation changing with hooks. I called the orientation hook from app.tsx and I want to update everything(theme,style in component) that uses widthPercentageToDP() function. How can I achieve this. I can't figured out.
useOrientation.tsx
export let { width, height } = Dimensions.get("window");
const heightPercentageToDP = (heightPercent: string | number): number => {
// Parse string percentage input and convert it to number.
const elemHeight =
typeof heightPercent === "number"
? heightPercent
: parseFloat(heightPercent);
// Use PixelRatio.roundToNearestPixel method in order to round the layout
// size (dp) to the nearest one that correspons to an integer number of pixels.
return PixelRatio.roundToNearestPixel((height * elemHeight) / 100);
};
export const useScreenDimensions = () => {
const [screenData, setScreenData] = useState({});
useEffect(() => {
setScreenData({orientation:currentOrientation()});
Dimensions.addEventListener("change", (newDimensions) => {
width = newDimensions.screen.width;
height = newDimensions.screen.height;
setScreenData({orientation:currentOrientation()}); // can be used with this height and width
//console.log(newDimensions.window);
});
return () => Dimensions.removeEventListener("change", () => {});
});
return {
width,height,
screenData
};
};
Theme file
const theme = {
spacing: {
m:widthPercentageToDP("2%") // it must be updated when orientation changes.
},
borderRadii: {
s:widthPercentageToDP("5%") // it must be updated when orientation changes.
},
textVariants: {
body:{
fontSize:widthPercentageToDP("%3"),
}
},
};
App.tsx
const {screenData} = useScreenDimensions();
console.log(screenData)
return (
<ThemeProvider>
<LoadAssets {...{ fonts, assets }}>
<Example/>
</LoadAssets>
</ThemeProvider>
);
}
Example.tsx
export const Example = ({}) => {
return (
<Box>
<Text variant="body">hey</Text>
{/* // it must be updated when orientation changes. */}
<View style={{width:widthPercentageToDP("40%")}}/>
</Box>
);
}
Box and theme come from theme.tsx file. Text component accepts variant prop that defined in theme.tsx
Using react-native-orientation you can do what you want, then the device orientation changes.
Example:
import Orientation from 'react-native-orientation';
export default class AppScreen extends Component {
componentWillMount() {
const initial = Orientation.getInitialOrientation();
if (initial === 'PORTRAIT') {
// do something
} else {
// do something else
}
},
componentDidMount() {
// this will listen for changes
Orientation.addOrientationListener(this._orientationDidChange);
},
_orientationDidChange = (orientation) => {
if (orientation === 'LANDSCAPE') {
// do something with landscape layout
} else {
// do something with portrait layout
}
},
componentWillUnmount() {
// Remember to remove listener to prevent memory leaks
Orientation.removeOrientationListener(this._orientationDidChange);
}

App stop working when Image Picker is opened in React Native

I am developing a React Native application using React Native. I am using react native image picker library, https://www.npmjs.com/package/react-native-imagepicker to pick up the images from the Gallery. But when I opened the image picker, my app stopped working and exited.
This is my code
import React from "react";
import { CameraRoll, View, Text, Button, Alert, Image } from "react-native";
import ImagePicker from "react-native-image-picker";
// More info on all the options is below in the API Reference... just some common use cases shown here
const options = {
title: "Select Avatar",
customButtons: [{ name: "fb", title: "Choose Photo from Facebook" }],
storageOptions: {
skipBackup: true,
path: "images"
}
};
class Gallery extends React.Component {
constructor(props) {
super(props);
this.state = {
url:"https://www.designevo.com/res/templates/thumb_small/terrible-black-bat-icon.png",
avatarSource: null
};
}
saveToCameraRoll = () => {
let { url } = this.state;
};
_handlePickImageButton = () => {
ImagePicker.showImagePicker(options, response => {
console.log("Response = ", response);
if (response.didCancel) {
Alert.alert("User cancelled image picker")
} else if (response.error) {
//console.log("ImagePicker Error: ", response.error);
Alert.alert("ImagePicker Error:");
} else if (response.customButton) {
//console.log("User tapped custom button: ", response.customButton);
Alert.alert("Custom button");
} else {
const source = { uri: response.uri };
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
});
}
});
};
render() {
return (
<View>
<Button
onPress={() => {
this._handlePickImageButton();
}}
title="Pick a image"
>
Pick image
</Button>
<Image source={this.state.avatarSource} />
</View>
);
}
}
export default Gallery;
What is wrong with my code? Also, I did not get any error info in the console as in the screenshot attached below.
I tried, opening in this way too
ImagePicker.launchImageLibrary(options, (response) => {
//nothing implemented yet
});
It just stopped working.
I added the following permission in the plist as well:
I tried this too
const options = {
noData: true
};
ImagePicker.launchImageLibrary(options, (response) => {
});
I found the issue. The problem was in the plist. When I was adding the permissions, I just copy-pasted from a post. Might be something was wrong with it. When I typed in the permissions in the XCode, I saw the suggestion box, so I just clicked on the suggestion box and added the description for each permission as below.
As you can see in the above screenshot, the String value in the Type column is grayed out and cannot be changed. In the screenshot attached in the question, those values can be changed. That is the difference.

How do I go back in webview? I am using the react-navigation package in react-native

I installed the react-navigation package in react-native
I have implemented tab navigation and one of them is implemented in webview format.
My problem is that if I press the back physical button on Android, I go from the app itself to the previous tab, not back from the webview.
I've already applied the back button for the webview on the internet, but I have not done that.
I tried to display the onNavigationStateChange log when debugging, but it was not updated when url was moved after it was loaded at first startup. Here is the code I implemented:
import React from "react";
import {BackHandler} from "react-native";
import {WebView} from "react-native-webview";
class SermonScreen extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
header: null
};
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
_onNavigationStateChange(navState) {
console.log(navState);
this.setState({
canGoBack: navState.canGoBack
});
}
handleBackButton = () => {
console.log(this.state);
if (this.state.canGoBack === true) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<WebView
source={{uri: 'https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos' }}
ref={(webView) => this.webView = webView}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
/>
);
}
}
export default SermonScreen;
Following the official webview documnentation you could try to do this: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#intercepting-hash-url-changes
In general you were almost there, however the way the YT navigation works made it impossible to be caught via the onNavigationStateChange, that's why we inject a JS code that intercepts these hash changes and posts a message to the parent component, we then catch it inside the onMessage handler and set the state variable properly. Copying the injectedJavaScript and onMessage properties to your example should solve your problem.
I prepared a component for you that seems to do what is needed:
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, { Fragment } from "react";
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
BackHandler,
StatusBar
} from "react-native";
import { WebView } from "react-native-webview";
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions
} from "react-native/Libraries/NewAppScreen";
class App extends React.Component {
constructor(props) {
super(props);
this.startingUrl =
"https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos";
this.handleBackButton = this.handleBackButton.bind(this);
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
}
handleBackButton = () => {
console.log(this.state);
const { canGoBack } = this.state;
if (canGoBack) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<Fragment>
<WebView
source={{ uri: this.startingUrl }}
style={{ marginTop: 20 }}
ref={webView => (this.webView = webView)}
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange');
});
})();
true;
`}
onMessage={({ nativeEvent: state }) => {
if (state.data === "navigationStateChange") {
// Navigation state updated, can check state.canGoBack, etc.
this.setState({
canGoBack: state.canGoBack
});
}
}}
/>
</Fragment>
);
}
}
export default App;
The response above was perfect. I set the state true for canGoBack though; I was getting a null error, so:
constructor(props) {
super(props);
this.startingUrl = "https://app.vethorcardpag.com.br/GIF/login/0/";
this.state = {
canGoBack : true
}
this.handleBackButton = this.handleBackButton.bind(this);
}
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}
Original answer
https://stackoverflow.com/a/74500469/7823800

Auto Login Issue in React-native IOS &Android

I am developing a sample application with React Native. My issue is that I am getting login credentials saved in localstorage() async storage in login page that values getting in index.ios.js file but values shows null.
Any one please tell me, when I login to page go_to_main, after that shows login screen and after main screen This is my main issue
Show can I get the values in index.ios.js file. Here is my code:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
AsyncStorage,
View
} from 'react-native';
import NavigationExperimental from 'react-native-deprecated-custom-components';
var loginScene //= require('./src/bundles/Login/scenes/LoginScene/index.js');
var SplashScreen = require('#remobile/react-native-splashscreen');
class SampleApp extends Component {
constructor(props) {
super(props);
this.state = {
email:'',
userName:''
}
}
componentWillMount() {
AsyncStorage.getItem('Email',(err,Email)=>{
if(Email == null ){
}
else{
AsyncStorage.getItem('userName',(err,userName)=>{
if(userName == null ){
}
else{
this.setState({
email:Email,
userName:userName
})
}
}).done()
}
}).done()
}
render() {
if(this.state.email != null){
loginScene = require('./src/bundles/Dashboard/scenes/koopiMainScene/index.js');
}
else{
alert("email1111:"+this.state.email)
loginScene = require('./src/bundles/Login/scenes/LoginScene/index.js');
}
return (
<View style={{flex:1,backgroundColor:'white'}}>
<NavigationExperimental.Navigator
initialRoute={{
component:loginScene,
passProps:{menuTitle:this.state.userName, Email:this.state.email}
}}
configureScene={(route) => ({
...NavigationExperimental.Navigator.SceneConfigs.HorizontalSwipeJump,
gestures: false
})}
renderScene={(route, navigator) =>{
return <route.component navigator={navigator} {...route.passProps} />;
}}/>
</View>
);
}
}
AppRegistry.registerComponent('SampleApp', () =>SampleApp);
I don'nt see your code where you are storing the value in the localstorage
so according to my View you can this way to store and get value form localstorage
AsyncStorage.setItem('user', JSON.stringify(email));
and for getting data you should use the parse method
AsyncStorage.getItem('user', (err, result) => {
const usermail = JSON.parse(result);
console.log(useremail)
});
By this type you would not get your value
may be this can help you