Moonlight
Moonlight

Reputation: 101

How to calculate content height of WKWebView?

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!

Upvotes: 10

Views: 18595

Answers (4)

natanavra
natanavra

Reputation: 2150

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;
}];

Upvotes: 3

黄少斌
黄少斌

Reputation: 1

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)
        })
    })
}

Upvotes: 0

morizotter
morizotter

Reputation: 1974

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.

Upvotes: 11

arsenius
arsenius

Reputation: 13266

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];
      }
}

Upvotes: 2

Related Questions