10

For UIWebView, we can get content height by this:

[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"]

But WKWebView doesn't have this method and webView.scrollView.contentSize.height is not right.

(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
    {
    }

Thank you for your help!

Neithrik
  • 2,002
  • 2
  • 20
  • 33
Moonlight
  • 101
  • 1
  • 1
  • 6
  • WKWebView has - evaluateJavaScript:completionHandler: instead. It's asynchronous online UIWebView's method. You will need to modify your code accordingly. – zambrey Apr 23 '15 at 21:57
  • Hi, Do you find any solution for this issue ? Thanks, – Nada Gamal Sep 03 '17 at 08:43
  • Possible duplicate of [How to determine the content size of a WKWebView?](https://stackoverflow.com/questions/27515236/how-to-determine-the-content-size-of-a-wkwebview) – BB9z Nov 27 '18 at 02:26

4 Answers4

11

I solved this problem with KVO.

First, addObserver for WKWebView's scrollView's contentSize like this:

addObserver(self, forKeyPath: "webView.scrollView.contentSize", options: .New, context: nil)

And next, receive the change like this:

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
    if keyPath == "webView.scrollView.contentSize" {
        if let nsSize = change[NSKeyValueChangeNewKey] as? NSValue {
            let height = nsSize.CGSizeValue().height
            // Do something here using content height.
        }
    }
}

It's easy to get webview's content height.

Don't forget to remove observer:

removeObserver(self, forKeyPath: "webView.scrollView.contentSize", context: nil)

I worte this line in deinit.

morizotter
  • 1,926
  • 2
  • 24
  • 34
  • webView.addObserver(self, forKeyPath: "webView.scrollView.contentSize", options: .New, context: nil) addObserver: forKeyPath:@"webView.scrollView.contentSize" options:1 context:0x0] was sent to an object that is not KVC-compliant for the "webView" property.' – orazz Nov 23 '15 at 08:37
  • 1
    @orazz it will work only if your view controller (self) has property named `webView`. – Timur Bernikovich Dec 23 '16 at 10:47
  • 1
    I think this only works if the webView content is larger than the webView. if the content is smaller, you get the frame of the webView. Trying to figure out a good way to resize the webView to get proper sizes. – Ryan Poolos Jun 15 '17 at 20:14
3

EDIT:

To get the best possible height calculation, in the end I did a few things to get the best height:

  1. I set the size of the webview to be really large and then load the content.
  2. Once the content is loaded (KVO notification) I resize the view using the method below.
  3. I then run the size function again and I get a new size and resize my view once again.

The above got the most accurate results for me.

ORIGINAL:

I've tried the scroll view KVO and I've tried evaluating javascript on the document, using clientHeight, offsetHeight, etc...

What worked for me eventually is: document.body.scrollHeight. Or use the scrollHeight of your top most element, e.g. a container div.

I listen to the loading WKWebview property changes using KVO:

[webview addObserver: self forKeyPath: NSStringFromSelector(@selector(loading)) options: NSKeyValueObservingOptionNew context: nil];

And then:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if(object == self.webview && [keyPath isEqualToString: NSStringFromSelector(@selector(loading))]) {
        NSNumber *newValue = change[NSKeyValueChangeNewKey];
        if(![newValue boolValue]) {
            [self updateWebviewFrame];
        }
    }
}

The updateWebviewFrame implementation:

[self.webview evaluateJavaScript: @"document.body.scrollHeight" completionHandler: ^(id response, NSError *error) {
     CGRect frame = self.webview.frame;
     frame.size.height = [response floatValue];
     self.webview.frame = frame;
}];
natanavra
  • 2,100
  • 3
  • 18
  • 24
2

You can get it like this:

self.webview.navigationDelegate = self;

- (void)webView:(WKWebView *)webview didFinishNavigation:(WKNavigation *)navigation{
     // webview.scrollView.contentSize will equal {0,0} at this point so wait
     [self checkIfWKWebViewReallyDidFinishLoading];
}


- (void)checkIfWKWebViewReallyDidFinishLoading{
      _contentSize = _webview.scrollView.contentSize;
      if (_contentSize.height == 0){
           [self performSelector:@selector(WKWebViewDidFinishLoading) withObject:nil afterDelay:0.01];
      }
}
arsenius
  • 12,090
  • 7
  • 58
  • 76
0

WKWebView didn't finish loading, when didFinishNavigation is called - Bug in WKWebView? WKWebView doesn't use delegation to let you know when content loading is complete. You can create a new thread to check the status of WKWebView.

override func viewDidLoad() {
    super.viewDidLoad()
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {  
        while(self.webView?.loading == true){
            sleep(1)
        }            
        dispatch_async(dispatch_get_main_queue(), {
            print(self.webView!.scrollView.contentSize.height)
        })
    })
}
Community
  • 1
  • 1
黄少斌
  • 1
  • 1
  • 2
    Please explain your answer in a little more detail for it to be helpful to more people than just a copy -> paste -> "it works!" -> we're done here! - style. – thepio May 17 '16 at 07:47