I write a simple iOS program to show image with React Native.
'use strict';
var React = require('react-native');
var {
Image
} = React;
var styles = React.StyleSheet.create({
base: {
height: 400,
width: 400
}
});
class SimpleApp extends React.Component {
render() {
return (
<Image
style={styles.base}
source={require('image!k')}
//source={{uri: 'http://news.xinhuanet.com/world/2015-05/09/127782089_14311512601821n.jpg'}}
/>
)
}
}
React.AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
But I got the message from iPad screen:
"Failed to print error:","'undefined' is not an object (evaluating 'stackString.split')"
When I change the code to use image url
//source={require('image!k')}
source={{uri: 'http://news.xinhuanet.com/world/2015-05/09/127782089_14311512601821n.jpg'}}
I only get a red rect border.
When I use another js file, everything works well."Hello World" can show on iPad screen.
'use strict';
var React = require('react-native');
var {
Text,
} = React;
class SimpleApp extends React.Component {
render() {
return (
<Text>Hello World</Text>
)
}
}
React.AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
Make sure you are using a recent version of React Native. With React Native 0.4.4 following works:
Local image
<Image
style={styles.base}
source={require('image!tabnav_settings')}
/>
Also make sure you added the image you want to use to your image assets:
Remote Image
<Image
style={styles.base}
source={{uri: 'http://i.stack.imgur.com/DfkfD.png'}}
/>
As #potench pointed out in the comments, loading static images with a .jpg extension doesn't currently work. But if you just rename your jpg to png everything works fine.
See also https://github.com/facebook/react-native/issues/521
Related
I currently have a KeyboardAvoidingView with a hard-coded keyboardVerticalOffset of 64. This works fine on the iPhone but is about 20px short on the iPhone X.
The component looks like this:
<KeyboardAvoidingView behavior='padding' keyboardVerticalOffset={ 64 }>
<View style={ styles.messageList }>
...
</View>
<View style={ styles.messageInput }>
...
</View>
</KeyboardAvoidingView>
Is there a better way to determine what keyboardVerticalOffset should be than hard coding a value? Is there something else I could be doing differently with component placement? I'm open to any suggestions.
iPhone 8
iPhone X
This is caused by the status bar height being different for iphoneX. (you also get the same issue with other iphones if you toggle the 'in-call' status bar using ⌘Y in the simulator).
You can get the status bar height and use this to set the keyboardVerticalOffset value of the KeyboardAvoidingView. (in our case this was 44 + statusBarHeight)
import React, {Component} from 'react';
import {KeyboardAvoidingView, NativeModules, StatusBarIOS} from 'react-native';
const {StatusBarManager} = NativeModules;
export class IOSKeyboardAvoidingView extends Component {
state = {statusBarHeight: 0};
componentDidMount() {
StatusBarManager.getHeight((statusBarFrameData) => {
this.setState({statusBarHeight: statusBarFrameData.height});
});
this.statusBarListener = StatusBarIOS.addListener('statusBarFrameWillChange', (statusBarData) => {
this.setState({statusBarHeight: statusBarData.frame.height});
});
}
componentWillUnmount() {
this.statusBarListener.remove();
}
render() {
const {style, children} = this.props;
return (
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={44 + this.state.statusBarHeight}
style={style}
>{children}
</KeyboardAvoidingView>
);
}
}
Please refer to : https://stackoverflow.com/a/51169574/10031014 for similar issues
I have used a custom component to overcome this situation.
import React from "react";
import {Animated, Keyboard} from "react-native";
export default class KeyboardAwareComponent extends React.Component {
constructor(props) {
super(props)
this.keyboardHeight = new Animated.Value(0);
}
componentWillMount () {
this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardWillShowSub.remove();
this.keyboardWillHideSub.remove();
}
keyboardWillShow = (event) => {
Animated.parallel([
Animated.timing(this.keyboardHeight, {
duration: event.duration,
toValue: event.endCoordinates.height,
})
]).start();
};
keyboardWillHide = (event) => {
Animated.parallel([
Animated.timing(this.keyboardHeight, {
duration: event.duration,
toValue: 0,
})
]).start();
};
render(){
const {children, style, ...props} = this.props
return(
<Animated.View style={[{flex:1,alignItems:'center',paddingBottom: this.keyboardHeight},style]} {...props}>
{children}
</Animated.View>
);
}
}
Just use the component "KeyboardAwareComponent" as a root component of any page. It will automatically adjust the view when keyboard will show or hide.
Example:---
YourComponent extends React.Component{
render(){
<KeyboardAwareComponent>
{Your child views}
</KeyboardAwareComponent>
}
}
So I did a quick check, given my understanding of how to do this in native iOS, and it seems like in newer versions of react native, you can do this relatively easily.
There do seem to be a couple of options, depending on your flexibility needs.
First, have you tried using KeyboardAvoidView instead of a standard container View without specifying keyboardVerticalOffset?
Another option that gives you much more control (similar to what I would do in a native iOS app) is to use the Keyboard module to create listeners on the keyboard events.
componentWillMount () {
this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardWillChangeSub = Keyboard.addListener('keyboardWillChangeFrame', this.keyboardWillChange);
this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardWillShowSub.remove();
this.keyboardWillChangeSub.remove();
this.keyboardWillHideSub.remove();
}
This would allow you to get the keyboard height from the event parameter:
keyboardWillShow = (event) => {
Animated.parallel([
Animated.timing(this.keyboardHeight, {
duration: event.duration,
toValue: event.endCoordinates.height,
}),
Animated.timing(this.imageHeight, {
duration: event.duration,
toValue: IMAGE_HEIGHT_SMALL,
}),
]).start();
};
Repeat something similar for keyboardWillChange and keyboardWillHide.
For a better, more detailed explanation of your options, see this page:
https://medium.freecodecamp.org/how-to-make-your-react-native-app-respond-gracefully-when-the-keyboard-pops-up-7442c1535580
I think the best first test would be to try to remove the keyboardVerticalOffset before trying to add code to handle the keboard events.
I'm trying to use Canvas taken from https://github.com/iddan/react-native-canvas
However, when trying to generate, it seems that the WebView creates the following error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'Canvas'
I've attached A snippet from the index.ios.js containing Canvas. Any help as to why the WebView generates this error would be super helpful (It works if you remove WebView and just leave View).
import React, { PropTypes, Component, WebView } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
Image,
TouchableHighlight,
} from 'react-native';
import {
StackNavigator,
} from 'react-navigation';
import SignatureCapture from 'react-native-signature-capture';
const Sound = require('react-native-sound');
// TEST
class Canvas extends Component {
propTypes: {
context: React.PropTypes.object,
render: React.PropTypes.func.isRequired
};
render() {
var contextString = JSON.stringify(this.props.context);
var renderString = this.props.render.toString();
return (
<View>
<WebView
automaticallyAdjustContentInsets={false}
contentInset={{top: 0, right: 0, bottom: 0, left: 0}}
html={'<style>*{margin:0;padding:0;}canvas{position:absolute;transform:translateZ(0);}</style><canvas></canvas><script>var canvas = document.querySelector("canvas");(" + renderString + ").call(" + contextString + ", canvas);</script>'}
opaque={false}
underlayColor={'transparent'}
style={this.props.style}/>
</View>
);
};
};
What version of RN are you using? On 0.42 it says html prop is deprecated so you would have to use source={html: string, baseUrl: string}. Also I think it might be having trouble defining one of your jsx elements, I'm thinking it might be the
Try to add your html code in a file and call source={{ uri: require('./myfile.html')}} . Use source prop instead html. html is deprecated. Verify the docs https://facebook.github.io/react-native/docs/webview.html
I'm building a simple ReactNative app for iOS and I'm trying to add a background image. It seems like there is no backgroundImage tag, and while I did manage to show the image on the page once, I can't repeat it throughout the page like you can with CSS. Any suggestions?
iOS
Images on iOS now include a repeat resizeMode property.
Use it like so:
<Image
src={/* whatever your source is */}
resizeMode="repeat"
/>
Android
June 2018 Update:
Starting from react-native 0.56 Android Images also support the repeat prop. (https://github.com/facebook/react-native/commit/0459e4ffaadb161598ce1a5b14c08d49a9257c9c)
Before June 2018:
On Android the repeat property does NOT work so:
you'll have to use something like Shiraman's answer
External lib:
There's now this great project called react-native-bgimage (created by Alex Melanchenko) which works well:
Here's how I use it:
import RepeatingPattern from 'react-native-bgimage';
<RepeatingPattern
drawable="block_pattern"
style={{
height: 45,
}}
/>
and then I add a pattern png file in android/app/src/main/res/drawable/block_pattern.png
Despite that this question is pretty old I would like to put my two cents in. It is also can be done via <ImageBackground> component (reference).
Example:
<ImageBackground source={require('./path/to/image.png')} style={{width: '100%', height: '100%'}} imageStyle={{resizeMode: 'repeat'}}>
// Your inside content goes here
</ImageBackground>
Don't forget to import component in the beginning of file, e.g. import { ImageBackground } from 'react-native';
I would like to extend Sriraman answer. To make the repeated images as the background, you need to take an addtional step of adding a new view and making its position absolute and background transparent, then adding all the other components inside of it.
var React = require('react-native');
var Dimensions = require('Dimensions');
var {
Image
} = React;
var RepeatImage = React.createClass({
render: function(){
var images = [],
imgWidth = 7,
winWidth =Dimensions.get('window').width;
for(var i=0;i<Math.ceil(winWidth / imgWidth);i++){
images.push((
<Image source={{uri: 'http://xxx.png'}} style={} />
))
}
return (
<View style={{flex:1,flexDirection:'row'}}>
{
images.map(function(img,i){
return img;
})
}
<View style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}>
{/*Place all you compoents inside this view*/}
</View>
</View>
)
}
});
You can't repeat background image like CSS in React Native. But, You can achieve it by iterating the image like
var React = require('react-native');
var Dimensions = require('Dimensions');
var {
Image
} = React;
var RepeatImage = React.createClass({
render: function(){
var images = [],
imgWidth = 7,
winWidth =Dimensions.get('window').width;
for(var i=0;i<Math.ceil(winWidth / imgWidth);i++){
images.push((
<Image source={{uri: 'http://xxx.png'}} style={} />
))
}
return (
<View style={{flex:1,flexDirection:'row'}}>
{
images.map(function(img,i){
return img;
})
}
</View>
)
}
});
In React Native, when using the WebView component, it starts to load the external content at the moment when the component will be rendered.
To increase performance in the application, I have tried to pre-fetch the external HTML so that it is ready when the component will be rendered. It seems like it is only an actual call to the render method will cause the loading to start and this is only controlled by what is rendered on the screen. I suppose React Native has no concept of shadow DOM that could be used to call the render method a head of time.
Trying to manipulate the lifecycle methods does also not work and is probably not a correct way of doing it either?
I have also tried to do a fetch() of the external HTML-content, with the right user-agent in the header, and pass the responseText to the WebComponent. This sometimes works for some sites of sources, but for others i run into ACAP (Automated Content Access Protocol) issues, to this is not the preferred solution.
Is there a way to pre-fetch external HTML content to a WebView component so that it displays faster?
fetch method runs on react side, fetch keep cache but that available for react apis and there component. WebView has there own caching concept. It's a browser. Caching of fetch will not avaialble for WebView. For faster loading by pre loaded data, You should fetch data by WebView instance of fetch api.
You can create a hidden WebView by setting width and height 0 and load your site on that. This will load your site on ViewView and keep cache, that will available for next time loading.
Here is a sample
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
WebView,
Alert,
ActivityIndicator,
} from 'react-native';
// const url = 'https://github.com/facebook/react-native'
// const url = 'https://in.yahoo.com/?p=us'
const url = 'https://www.facebook.com/'
class TestWebView extends Component {
render() {
var renderTime = Date.now();
return (
<WebView
source={{uri: url}}
style={{marginTop: 20, flex: 1}}
onLoad={() => {
Alert.alert('On load event', `Loading time : ${Date.now() - renderTime}`)
}}
/>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props)
this.state = {
isLoaded: false,
}
}
render() {
if (this.state.isLoaded) {
return (
<TestWebView />
)
}
return (
<View style={styles.container}>
<View style={{height: 0, width: 0}}>
<WebView
source={{uri: url}}
onLoad={() => {
this.setState({isLoaded: true})
}}
/>
</View>
<ActivityIndicator />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
I test this. After first time loaded data on WebView, It reduce 70% loading on actual WebView where we want to show to user.
Hi I know it's a known issue about the auto height of webview in react native,
and I have tried all the possibles solutions I've found on the internet such as :
https://gist.github.com/epeli/10c77c1710dd137a1335
https://github.com/danrigsby/react-native-web-container/blob/master/index.js
and all the solutions suggested in:
React native: Is it possible to have the height of a html content in a webview?
But unfortunately none of these seems to work for me,
I understand that the workaround they all suggest is to set the title to the height, but in my case it seems that the title always stays the same which is :
"text/html ...." and the rest of my html.
I get the html content from an API, it comes without a body, head or html tags, I've also tried adding these tags manually to the html and nothing seems to work.
I would love to hear if anyone else had that problem and how did it get fixed.
I wrap WebView inside a View, and set the height from the View.
<View style={{ height: 200 }}>
<WebView
automaticallyAdjustContentInsets={false}
source={{uri: 'https://player.vimeo.com/video/24156534?title=0&byline=0&portrait=0'}}
/>
</View>
I just follow this guide: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#communicating-between-js-and-native and succeeded in my work. Here is solution:
1. Define script to send document height to native env after loaded website.
2. Handle onMesssage of webview component and reset Height via state.
const webViewScript = `
setTimeout(function() {
window.postMessage(document.documentElement.scrollHeight);
}, 500);
true; // note: this is required, or you'll sometimes get silent failures
`;
...
constructor(props) {
super(props);
this.state = {
webheight:100,
}
...
<WebView style={{height: this.state.webheight}}
automaticallyAdjustContentInsets={false}
scrollEnabled={false}
source={{uri: "http://<your url>"}}
onMessage={event => {
this.setState({webheight: parseInt(event.nativeEvent.data)});
}}
javaScriptEnabled={true}
injectedJavaScript ={webViewScript}
domStorageEnabled={true}
></WebView>
Hope that help!
A reliable implementation of this behavior is with useAutoheight hook from #formidable-webview/webshell library.
The latter allows to inject "features" into WebViews, e.g. scripts and behaviors.
In this example, we will use 3 features + the aforementioned hook:
HandleHTMLDimensionsFeature which is required by useAutoheight hook to get document size updates;
ForceResponsiveViewportFeature to work around mobile virtual viewport;
ForceElementSizeFeature to work around cyclic size constraints
This component should work with any webpage.
import React from 'react';
import makeWebshell, {
HandleHTMLDimensionsFeature,
ForceResponsiveViewportFeature,
ForceElementSizeFeature,
useAutoheight
} from '#formidable-webview/webshell';
import WebView from 'react-native-webview';
const Webshell = makeWebshell(
WebView,
new HandleHTMLDimensionsFeature(),
new ForceResponsiveViewportFeature({ maxScale: 1 }),
new ForceElementSizeFeature({
target: 'body',
heightValue: 'auto',
widthValue: 'auto'
})
);
export default function ResilientAutoheightWebView(props) {
const { autoheightWebshellProps } = useAutoheight({
webshellProps: props
});
return <Webshell {...autoheightWebshellProps} />;
}
More resources:
Try this on Expo
Full guide here.
Using postMessage and onMessage like below worked for me perfectly.
Credit to iamdhj
onWebViewMessage = (event: WebViewMessageEvent) => {
this.setState({webViewHeight: Number(event.nativeEvent.data)})
}
render() {
return (
<ScrollView>
<WebView
style={{ height: this.state.webViewHeight }}
source={{html: '...'}}
onMessage={this.onWebViewMessage}
injectedJavaScript='window.ReactNativeWebView.postMessage(document.body.scrollHeight)'
/>
</ScrollView>
)
}
The WebView has default styles. If you want to set height, you also need to add flex: 0, as stated in the documentation:
Please note that there are default styles (example: you need to add flex: 0 to the style if you want to use height property).
I made a little component to make this functionality reusable if it helps anyone!
import React, { useState } from "react";
import WebView from "react-native-webview";
const DynamicHeightWebView = (props) => {
const [height, setHeight] = useState(0);
const webViewScript = `
setTimeout(function() {
window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight);
}, 500);
true; // note: this is required, or you'll sometimes get silent failures
`;
return <WebView
{...props}
style={{
...props.style,
height: height,
}}
automaticallyAdjustContentInsets={false}
scrollEnabled={false}
onMessage={event => {
setHeight(parseInt(event.nativeEvent.data));
}}
javaScriptEnabled={true}
injectedJavaScript ={webViewScript}
domStorageEnabled={true}
useWebKit={true}
/>
}
export default DynamicHeightWebView;
Apparently the problem was I had javaScriptEnabled={false}.
After enabling it everything worked.
I waste whole day to fix the height issue but in the end I had to shift to another library
This one is easy and good
https://github.com/archriss/react-native-render-html
You can get the content height by injecting the JS code as suggested by #ken-ratanachai-s. Although, You will experience certain irregularities in some devices (Extra height after the content). This is becuase the javascript returns the content height in pixels, but we need to use display points in react native. To fix this, Divide the height from javascript with the pixel ratio as follows.
import { WebView, PixelRatio } from 'react-native'
const [webviewHeight, setWebviewHeight] = useState(0)
const onProductDetailsWebViewMessage = event => {
setWebviewHeight(Number(event.nativeEvent.data)/PixelRatio.get())
}
return <WebView
originWhitelist={['*']}
style={{ height: productDetailsWebviewHeight }}
onMessage={onProductDetailsWebViewMessage}
injectedJavaScript='window.ReactNativeWebView.postMessage(document.body.scrollHeight)'
source={{ html: "..." }}
/>
Pixel ratio ref.: https://reactnative.dev/docs/pixelratio
Courtesy: https://stackoverflow.com/a/65976827/5321660
use package react-native-autoheight-webview
I recommend react-native-autoheight-webview.
it perfect work for me.
https://github.com/iou90/react-native-autoheight-webview
UPDATE:
Best answer is #Ken Ratanachai S.'s answer.
https://stackoverflow.com/a/65976827/9757656