18
votes

I have a WKWebView app running on iOS8 on iPad (standard iPad UserAgent : "Mozilla/5.0 (iPad; CPU OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B436 Safari/600.1.4"). I have tried all delegates and listeners I can think of to try and detect when a page has finished loading, every time. Here is the problem:

  1. Open your WKWebView and go to Google. The following are called:

    _decidePolicyForNavigationAction
    _didStartProvisionalNavigation
    _ didFinishNavigation
    
  2. Type “YouTube” into Google: ONLY _decidePolicyForNavigationAction is called. No didStartProvisionalNavigation or didFinishNavigation

  3. Click on YouTube within Google. The following are called:

    _didStartProvisionalNavigation
    _decidePolicyForNavigationAction
    _ didFinishNavigation
    
  4. From now on, within YouTube nothing is called. Click on a YouTube video and no didStartProvisionalNavigation or didFinishNavigation. Also, webview.loading observer is no longer called. Even decidePolicyForNavigationAction is only called every now and then. HOWEVER… The webView.backForwardList.currentItem is updated after every click, so this must be detecting when the page has finished loading somehow?

This behavior happens on a lot of other sites too (Vimeo for example). I know this type of site is not updating the main frame every time, but is this lack of a capability to detect when loading has started/finished a ‘limitation’ of WKWebView in its current state?

What I want to achieve is: Is there another way to detect when navigation has COMPLETED, EVERY TIME within Java mobile websites like YouTube/Vimeo:

  • Using NSURLProtocol or similar?
  • Or maybe a way to detect when webView.backForwardList.currentItem is updated?

Thanks for any help.

2
Check out stackoverflow.com/questions/32660051/… I use the webview.scrollView.contentSize property to check on the height, and if it's zero, then I recall the respective function with a small delay until its set.peacer212
Thanks, but I am not trying to detect when the WKWebView appears on the screen - I want WKWebView to notify me when sites that use Java only (YouTube mobile, Vimeo mobile etc) BEGIN and END loading for clicks WITHIN their sites on iPhone. WKWebView doesn't call ANYTHING once YouTube has loaded on iPhone... You can continue to browse the site and no delegate methods are called.AT3D
The problem with web views is that, for sites you don't control (and could therefore insert specific event hooks into) the concept of "done loading" starts being very hard to define once you start trying to pin it down generically. Javascript loading and running in a page, as is typical of modern complex sites, can pull down content via non-page requests and then populate the page after the original HTML and its resources have fully downloaded and the DOM is built. That's what you're seeing in YT and Vimeo. The page isn't technically navigating anywhere.Ben Zotto

2 Answers

12
votes

I'm not sure what exactly you want to do when page have loaded, but hopefully observing these properties can help you:

webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)
webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)
webView.addObserver(self, forKeyPath: "title", options: .New, context: nil)
webView.addObserver(self, forKeyPath: "canGoBack", options: .New, context: nil)
webView.addObserver(self, forKeyPath: "canGoForward", options: .New, context: nil)
0
votes

If anyone is looking for solution how to use the observer, here is the solution:

webView.addObserver(self, forKeyPath: "title", options: .new, context: nil);
webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil);
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil);

Then use observeValue function and write a switch-case or if-else to find the keyPath.

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "title" {
        print(change ?? "No title")
        if change != nil, let currentTitle = change?[.newKey] as? String {
            titleLabel.text = currentTitle
        }
    } else if keyPath == "loading" {
        print("Loading")
        reloadButton.setImage(#imageLiteral(resourceName: "cancel.png"), for: .normal)
    } else if keyPath == "estimatedProgress" {
        print(wkWebView.estimatedProgress);
        progressView.progress = Float(wkWebView.estimatedProgress)
    }
}

Here I have created a demo project to explore more about WebKit WebView.