How to Determine the Content Size of a Uiwebview

How to determine the content size of a UIWebView?

It turned out that my first guess using -sizeThatFits: was not completely wrong. It seems to work, but only if the frame of the webView is set to a minimal size prior to sending -sizeThatFits:. After that we can correct the wrong frame size by the fitting size. This sounds terrible but it's actually not that bad. Since we do both frame changes right after each other, the view isn't updated and doesn't flicker.

Of course, we have to wait until the content has been loaded, so we put the code into the -webViewDidFinishLoad: delegate method.

Obj-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;

NSLog(@"size: %f, %f", fittingSize.width, fittingSize.height);
}

Swift 4.x

func webViewDidFinishLoad(_ webView: UIWebView) {
var frame = webView.frame
frame.size.height = 1
webView.frame = frame
let fittingSize = webView.sizeThatFits(CGSize.init(width: 0, height: 0))
frame.size = fittingSize
webView.frame = frame
}

I should point out there's another approach (thanks @GregInYEG) using JavaScript. Not sure which solution performs better.

Of two hacky solutions I like this one better.

How to determine the content size of a WKWebView?

I think I read every answer on this subject and all I had was part of the solution. Most of the time I spent trying to implement KVO method as described by @davew, which occasionally worked, but most of the time left a white space under the content of a WKWebView container. I also implemented @David Beck suggestion and made the container height to be 0 thus avoiding the possibility that the problem occurs if the container height is larger that that of the content. In spite of that I had that occasional blank space.
So, for me, "contentSize" observer had a lot of flaws. I do not have a lot of experience with web technologies so I cannot answer what was the problem with this solution, but i saw that if I only print height in the console but do not do anything with it (eg. resize the constraints), it jumps to some number (e.g. 5000) and than goes to the number before that highest one (e.g. 2500 - which turns out to be the correct one). If I do set the height constraint to the height which I get from "contentSize" it sets itself to the highest number it gets and never gets resized to the correct one - which is, again, mentioned by @David Beck comment.

After lots of experiments I've managed to find a solution that works for me:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
self.containerHeight.constant = height as! CGFloat
})
}

})
}

Of course, it is important to set the constraints correctly so that scrollView resizes according to the containerHeight constraint.

As it turns out didFinish navigation method never gets called when I wanted, but having set document.readyState step, the next one (document.body.offsetHeight) gets called at the right moment, returning me the right number for height.

UIWebView dynamic content size

This post has been updated for Swift 5 & WKWebView


So this is a really great function you wrote there, OP!

Here is just a shorter, more elegant version of your code:

// make sure to declare the delegate when creating your webView (add UIWebViewDelegate to class declaration as well)
myWebView.delegate = self

func webViewDidFinishLoad(webView: UIWebView) {
webView.frame.size.height = 1
webView.frame.size = webView.sizeThatFits(CGSize.zero)
}

Migrating to WKWebView

1) import WebKit

2) make your ViewController inherit from WKNavigationDelegate

3) hook up the WKWebView’s delegate: webView.navigationDelegate = self

4) implement the following protocol function:

webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

After migrating from UIWebView to WKWebView, above approach doesn’t seem to work anymore.

What you can do instead, is change the line with webView.sizeThatFits(CGSize.zero) to:

webView.frame.size = webView.scrollView.contentSize

The full code for WKWebView would then be:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.frame.size.height = 1
webView.frame.size = webView.scrollView.contentSize
}

UIWebView : Get Webview Content size?

When font change at that time change Webview height to 1 than get proper offsetHeight for Webview

webView1.frame.size.height = 1
let size : String = self.webView1.stringByEvaluatingJavaScript(from: "document.documentElement.offsetHeight")!

UIWebView frame height based on content height

The only really reliable way to determine the content height of a UIWebView is to do it via Javascript once the HTML is fully rendered.

The problem with getting the height in webViewDidFinishLoad: is, that this method is called right after the HTML is loaded into the UIWebView. But that does not mean that the HTML is already fully rendered. This might lead to random and wrong heights.

So to get the real height you have to listen to the onLoad event in the HTML DOM. Then send the height to the UIWebView.

You can find a detailed description on how to do this in this blog post that I wrote a while ago.

EDIT:

So this is a special case, where the HTML content is actually smaller than the UIWebView frame. Getting the height of the <body> does not work in this case, because the body always extends to the full frame.

You have to change the HTML a little bit. You have to add an element that wraps your HTML content:

<body>
<div id="wrapper">
<div id="content" contenteditable="true"> </div>
<div style="font-family: Helvetica"> Sample text </div>
</div>
</body>

Then in the Javascript snippet you return the height of that wrapper element:

<script type="text/javascript">
window.onload = function() {
window.location.href = "ready://" + document.getElementById('wrapper').offsetHeight;
}
</script>

This returns the correct height of your HTML content. Just on more thing: because the <body> element gets a default margin, you have to set the margin to 0:

<style type="text/css">
body {
margin:0;
color: purple;
background-color: #d8da3d;
}
</style>

Now you get the correct size and you can move your label according to the UIWebView's height.



Related Topics



Leave a reply



Submit