making a multiline, expanding TextInput with React-Native - ios

I'm working on a react-native app and need a TextInput that has similar functionality to the textview in the "messages" app on iOS—it should start out as one line and then gracefully expand to more lines until some limit (like 5 lines of text) and then start scrolling along to latest line as needed.
Took a look at the SlackTextViewController but a) seems like it has a lot of stuff that I don't want and b) I'd like to try to keep as much code in React (and out of objective-C/swift) as possible.
Edit: Just want to emphasize that I would prefer REACT (JAVASCRIPT) code, as stated above, rather than Objective-C or Swift.

I tried two different ways to do this today. Neither are the best but I thought I'd record my efforts in case they are helpful. They both definitely had the effect you are looking for, though sometime delayed with all the async communication.
1) Offscreen Text height
So just under the TextInput, I added a regular Text field with the same font and padding and such. I registered the onChange listener on the input and called setState({text: event.nativeEvent.text}). The Text filed got its value from the state. Both had onLayout Listeners. Basically, the goal was to get the height for the TextInput from the (unrestricted) Text. Then I hid the Text way offscreen
https://gist.github.com/bleonard/f7d748e89ad2a485ec34
2) Native Module
Really, I just needed the height of the content in the real UITextView. So I added a category to RCTUIManager as there are several methods there already that are helpful. I got rid of the hidden Text view. So onChange, I ask for the height and use that in the same way via the state.
https://gist.github.com/bleonard/6770fbfe0394a34c864b
3) Github PR
What I really hope is that this PR gets accepted. It looks to do something like this automatically.
https://github.com/facebook/react-native/pull/1229

Adding multiline={true} to a TextInput will allow scrolling if the amount of text exceeds the available space. You can then change the height of the TextInput by accessing the nativeEvent.contentSize.height of the event from the onChange prop.
class Comment extends Component {
state = {
text: '',
height: 25
}
onTextChange(event) {
const { contentSize, text } = event.nativeEvent;
this.setState({
text: text,
height: contentSize.height > 100 ? 100 : contentSize.height
});
}
render() {
return (
<TextInput
multiline
style={{ height: this.state.height }}
onChange={this.onTextChange.bind(this)}
value={this.state.text}
/>
);
}
}

As of Oct'17 there is a nice component from Wix to do this:
https://github.com/wix/react-native-autogrow-textinput
The usage can be very simple:
<AutoGrowingTextInput
style={styles.textInput}
placeholder="Enter text"
value={this.state.text}
onChangeText={this._handleChangeText}
/>
And there are some extra props like minHeight and maxHeight for example.
I'm using it on RN 0.47.2

Implement the UITextView's delegate method textViewDidChange and play with the rect
- (void)textViewDidChange:(UITextView *)textView {
CGSize constraintSize = CGSizeMake(textView.frame.size.width, MAXFLOAT);
CGRect textRect = [textView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:textView.font}
context:nil];
NSLog(#"Frame:%#", NSStringFromCGRect(textRect));
CGRect newRect = textView.frame;
newRect.size.height = textRect.size.height;
textView.frame = newRect;
}

#Groovietunes got almost all of it correct but as of 26-06-19 things have change a bit from his initial answer.
For one, the onChange prop on textinputs no longer returns the contentSize object, hence event.nativeEvent.contentSize will return undefined.
The fix for this is to use the onContentSizeChange prop. But there's a catch; onContentSizeChange runs only once when the component mounts thus making it impossible to dynamically get the height as it changes.
To fix this we need to make sure the value prop is set after onContentSizeChange in the hierarchy of props in order to keep obtaining the content size as the textinput grows.
At the end of it all i had a component that looked like this
<TextInput
placeholder={this.props.placeholder}
autoFocus={this.props.autoFocus}
style={[
styles.inputFlat,
{ height: this.state.height > 40 ? this.state.height : 40 }
]}
ref="inputFlat"
onFocus={() => {
this.toggleInput();
this.toggleButton();
}}
multiline={this.props.multiline}
onBlur={() => {
this.toggleInput();
this.toggleButton();
}}
onChangeText={this.props.onChangeText}
onContentSizeChange={event => {
const { contentSize } = event.nativeEvent;
this.setState({
height: contentSize.height
});
}}
value={this.state.value}
/>
if for some reason it seems broken as at the time of viewing this answer, refer to this documentation for any updates

Another solution is to check '\n' symbols and set the numberOfLines property.
Works for me.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {TextInput} from 'react-native';
export default class TextInputAutogrow extends Component {
constructor(props) {
super(props);
this._ref = null;
this.bindRef = this.bindRef.bind(this);
this.onChangeText = this.onChangeText.bind(this);
this.state = {
numberOfLines: this.getNumberOfLines()
};
}
bindRef(c) {
this._ref = c;
this.props.innerRef && this.props.innerRef(c);
}
getText() {
return typeof this.props.value === 'string' ?
this.props.value :
(
typeof this.props.defaultValue === 'string' ?
this.props.defaultValue :
''
);
}
getNumberOfLines(value) {
if (value === undefined) {
value = this.getText();
}
return Math.max(this.props.numberOfLines, value.split('\n').length - 1) + 1;
}
onChangeText(value) {
this.setState({numberOfLines: this.getNumberOfLines(value)})
}
render() {
return (
<TextInput
{...this.props}
ref={this.bindRef}
numberOfLines={this.state.numberOfLines}
onChangeText={this.onChangeText}
/>
)
}
}
TextInputAutogrow.propTypes = {
...TextInput.propTypes,
innerRef: PropTypes.func,
};
TextInputAutogrow.defaultProps = {
numberOfLines: 4,
};

For anyone wants an easy solution, using new versions of RN, currently using 0.63.4,
the team added autoGrow to the TextInput if it's multiline, which is the case.
Don't use height (StyeSheet property) for the component or the parent View/Views
Use minHeight, maxHeight instead, and if multiline is enabled, the TextInput will grow till reaching the maxHeight, then will be scrollable.
no calculation nor inner state needs to ba maintained.
Same thing apply for minWidth, maxWidth if you need to expand widly.

Since react-native-autogrow-textinput seems to be unmaintained, I would recommend using native-base <Input/> as it has properties that enables multiline and auto-height as follows:
import { Input } from 'native-base'
<Input
height={'auto'}
multiline={true}
// ...
/>

Related

Column does not vertically scroll although it was configured to

I am trying to add scrolling behaviour to a Column by setting verticalScroll(state = rememberScrollState()) modifier.
I checked out some examples in the official compose-jb repository, and it seems that that is the right way to do it, yet in my case the content is not scrollable.
Here the full code:
#Composable
#Preview
fun App() {
MaterialTheme {
// add scroll behaviour
val stateVertical = rememberScrollState(0)
Column(modifier = Modifier.verticalScroll(state = stateVertical)) {
repeat(100){
Text("item: $it")
}
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
Any ideas why it does not work in my case?
The Column is populated with 100 Text items, more than enough to exceed the default window height.
It actually works!
For some reason I was trying to use click and drag... which lead me to confusion.

Cannot turn autoCorrect to {false} in react native TextInput

On my TextInput change text, I detect whether the user pushed the # button for mentions.
onChangeText(text){
const suggestTrigger = text.match(/\B#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
this.setState({
searchQuery: searchQuery
})
}
Then, in my render, I do:
<TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
However, even when I do this, the autoCorrect does not turn off.
I still see this:
This is causing problems because oftentimes the system replaces the entire mention with a different word altogether.
How can I turn autoCorrect and autoCapitalize off when the user pushes the # button?
'
I have even tried rendering an entirely different , but the behavior remains.
TL;DR: you should close and re-launch your keyboard after the TextInput autoCorrect toggling value.
Buddy, this is not your fault, I had the same issue on autoFocus of react native TextInput component. I set a state name for the TextInput editable prop and then with the pressing pencil button I change the editable props. The designer told me after the TextInput made editable the cursor should be focused, so I use the isEditable state for autoFocus prop, see below:
state = {
isEditable: false
};
handleEdit = () => {
const { isEditable } = this.state;
this.setState({ isEditable: !isEditable });
};
<TextInput
autoFocus={isEditable}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
/>
Then I press the edit button and it turns to:
But it is not focused and the Keyboard didn't launch, I sought and found this link, the TextInput does not change/update some of its props after componentDidMount. ☹️. Also, it is not different in iOS or Android, both of them has this issue, I used ref to focus on this TextInput after the isEditable state made true. see following code:
<TextInput
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
componentDidUpdate() {
const { isEditable } = this.state;
if (isEditable) {
this.input.focus();
}
}
And your case:
Absolutely you can not use ref because the autoCorrect should render with react native and it is not like focus() and blur() so JavaScript cannot access to it.
I make a test shape for your case, I create another button like a star for toggling autoCorrect value alongside my edit button. the filled star means autoCorrect is true and the lined star means autoCorrect is false, now see the test area code and view:
state = {
isEditable: false,
correct: true
};
handleCorrect = () => {
const { correct } = this.state;
this.setState({ correct: !correct });
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
In the above photo, it is so clear the autoCorrect rendered as true, so it is enabled:
When I write a wrong Persian word the auto-correction show its suggestion, now time to press the star button:
Wow, the autoCorrection is false in the above situation but still we see the auto-correction of the cellphone. it is just like autoFocus it is rendered in the first render and after it, the TextInput could not change/update its props. suddenly I press edit button:
And I press the edit button again, so surely, you realized the autoCorrect is false now, ok now see what I saw:
The autoCorrect remained false by my double pressing edit button and now the auto-correction of device disappears completely. I don't know it is a bug or my bad understanding but I realized in this test area, for update autoCorrect value, we should do something after changing its value to close the iPhone keyboard and then re-launch it. the main thing that TextInput has issued is the launched keyboard.
For my test, when I pressed the edit button the editable prop of the TextInput is changed to false and the keyboard is closed, so when I pressed the edit button again, the TextInput get focused and keyboard re-launched with new autoCorrect value. This is The Secret.
Solution:
You should do something, to close and open again the iOS keyboard with the new autoCorrect value. for the test case that I wrote for your question, I decided to do a hybrid innovative solution, using ref and the callback of setState:
handleCorrect = () => {
const { correct } = this.state;
this.input.blur(); //-- this line close the keyboard
this.setState({ correct: !correct },
() => {
setTimeout(() => this.input.focus(), 50);
//-- above line re-launch keyboard after 50 milliseconds
//-- this 50 milliseconds is for waiting to closing keyborad finish
}
);
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
And after pressing the star button the keyboard close and re-launch and the auto-correction disappear completely.
Note: obviously, I summarized some other codes like destructuring and writing class or extends and etc for better human readability.
The problem isn't in your code completely(except Regex part which didn't work in my device) but how the React Native renders Keyboard.
I created a sample that along with your initial code also changes backgroundColor of the screen.
You will find that color changes instantly when '#' is entered whereas the keyboard doesn't.
Unless you reload the keyboard. For this, if you un-comment the code it dismisses keyboard once and once you tap back on textInput the new Keyboard without autoCorrect and autoCapitalize is shown.
state = {
searchQuery: null,
isFocused: true,
}
constructor(props) {
super(props);
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText = (val) => {
const suggestTrigger = val.match(/#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
// Un comment this to reload
// if(searchQuery && this.state.isFocused) {
// this.setState({
// isFocused: false
// });
// Keyboard.dismiss();
// // this.myTextInput.focus()
// }
this.setState({
searchQuery: searchQuery
})
}
render() {
const { searchQuery } = this.state
return (
<View style={[styles.container,
{
backgroundColor: searchQuery ? "red": "green"}
]}>
<TextInput
style={{width: 300, height: 50, borderWidth: 1}}
ref={(ref)=>{this.myTextInput = ref}}
autoCapitalize={searchQuery ? "none" : "sentences"}
autoCorrect={searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
</View>
);
}
Since now we know the main cause of error may be a better solution can be reached.
I have 2 suggestions:
First, have you tried using the autoCorrect fallback?
spellCheck={this.state.searchQuery ? false : true}
Second, have you tried with native code (Obj-C / Swift)?
import { Platform, TextInput, View } from 'react-native';
import { iOSAutoCorrect } from './your-native-code';
const shouldWork = Platform.OS === 'ios' ? <iOSAutoCorrect /> : <TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText} />
return (<View>{shouldWork}</View>);
In iOSAutoCorrect you should use a UITextView. Then set its proper value depending on your condition:
textField.autocorrectionType = UITextAutocorrectionTypeNo; // or UITextAutocorrectionTypeYes
I have free-coded, thus the code is untested and might contain bugs.

How to set height of React Native Webview? [duplicate]

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

React Native: How to animate opacity of ScrollView/ListView children as they enter/leave the viewport?

How is this done in React Native? I've tried using touchable opacity, but it doesn't work. And, there doesn't seem to be documentation about how to do this in ScrollView.
Thank you :)
You can do this by using Animated.
Check the first example in the docs below.
https://facebook.github.io/react-native/docs/animated.html
You will need to create a custom Item component that runs the animation on componentDidMount. For example, if you have a list of users, each item on the list should be a UserItem component.
class UserItem extends Component {
componentWillMount() {
this.animatedValue = new Animated.Value(0);
}
componentDidMount() {
this.startAnimation()
}
startAnimation() {
Animated.timing(this.animatedValue, {
toValue : 1,
duration : 500,
}).start()
}
render() {
return (
<Animated.View
style={[someStyles, { opacity: this.animatedValue }]}
>
<AnyThingYouNeedHere />
</Animated.View>
);
}
}
Then in your list you just need to use UserItem. You might want to add some delay for each item or something else.
Here's a great tutorial that shows you how to add animations when adding/removing items.
http://moduscreate.com/react-native-dynamic-animated-lists/
Good luck!

React native: webview height

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

Resources