当调用 didFinishNavigation 时,WKWebView 没有完成加载 - WKWebView 中的 Bug?

WKWebView didn't finish loading, when didFinishNavigation is called - Bug in WKWebView?

目标:网站加载完成后对WKWebView进行截图

采用的方法:

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
    let img = webView.screenCapture()
}

问题:

我在这里错过了什么?我查看了 WKWebView 的所有可能的委托函数,似乎没有其他东西代表 WKWebView 中内容加载的完成。如果有解决方法,将不胜感激


更新:添加我用来截取网页视图截屏的截屏代码

class func captureEntireUIWebViewImage(webView: WKWebView) -> UIImage? {

    var webViewFrame = webView.scrollView.frame
    if (webView.scrollView.contentSize != CGSize(width: 0,height: 0)){
    webView.scrollView.frame = CGRectMake(webViewFrame.origin.x, webViewFrame.origin.y, webView.scrollView.contentSize.width, webView.scrollView.contentSize.height)

    UIGraphicsBeginImageContextWithOptions(webView.scrollView.contentSize, webView.scrollView.opaque, 0)
    webView.scrollView.layer.renderInContext(UIGraphicsGetCurrentContext())
     var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()

     webView.scrollView.frame = webViewFrame         
     return image
    }

    return nil
 }

WKWebView 不使用委托来让您知道内容加载何时完成(这就是为什么您找不到适合您目的的任何委托方法的原因)。知道一个WKWebView是否还在加载的方法是使用KVO(key-value observing)观察它的loading属性。这样,当 loadingtrue 变为 false 时,您会收到通知。

这是一个循环动画 gif,显示了我对此进行测试时发生的情况。我加载一个 web 视图并通过 KVO 响应它的 loading 属性 来拍摄快照。上层视图为网页视图;较低的(压扁的)视图是快照。如您所见,快照确实捕获了加载的内容:

[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
    if (self->_webKitView.isLoading == true) {
        NSLog(@"Still loading...");
    }else {
        NSLog(@"Finished loading...");
        [timer invalidate];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self->_activityIndicator stopAnimating];
        });
    }
}];

我是这样解决的:

class Myweb: WKWebView {

    func setupWebView(link: String) {
        let url = NSURL(string: link)
        let request = NSURLRequest(URL: url!)
        loadRequest(request)
        addObserver(self, forKeyPath: "loading", options: .New, context: nil)
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        guard let _ = object as? WKWebView else { return }
        guard let keyPath = keyPath else { return }
        guard let change = change else { return }
        switch keyPath {
        case "loading":
            if let val = change[NSKeyValueChangeNewKey] as? Bool {
                if val {
                } else {
                    print(self.loading)
                    //do something!
                }
            }
        default:break
        }
    }

    deinit {
        removeObserver(self, forKeyPath: "loading")
    }
}

更新Swift3.1

override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    guard let _ = object as? WKWebView else { return }
    guard let keyPath = keyPath else { return }
    guard let change = change else { return }

    switch keyPath {
    case "loading":
        if let val = change[NSKeyValueChangeKey.newKey] as? Bool {
            //do something!
        }
    default:
        break
    }
}

检查页面内容是否从 swift 或 objective-c 加载不是一个好的选择,尤其是对于包含许多动态内容的非常复杂的页面。

一种更好的方式来通知您 ios 来自网页 javascript 的代码。这使它非常灵活和有效,您可以在 dom 或页面加载时通知 ios 代码。

您可以在此处查看我的 以获取更多信息。

对于那些仍在寻找答案的人,标记的答案是 BS,他只是强行让它被接受。

使用属性,

"loading"

webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!)

两者做同样的事情,表明是否加载了主要资源。

现在,这并不意味着整个 webpage/website 都已加载,因为它实际上取决于网站的实施。如果它需要加载 scriptsresources(图像、字体等)以使其可见,导航完成后您仍然什么也看不到, 因为网站的网络调用没有被 webview 跟踪, 只跟踪导航, 所以它不知道什么时候网站完全加载。

这里有很多人放弃了不完整的解决方案。 "loading" 上的观察者不可靠,因为当它切换到 NO 时,布局还没有发生,你无法准确读取页面大小。

根据实际页面内容,将 JS 注入视图以报告页面大小也可能存在问题。

WKWebView 显然使用滚动条(确切地说是 "WKWebScroller" 子类)。因此,最好的办法是监视该滚动条的 contentSize。

    - (void) viewDidLoad {
    //...super etc
    [self.webKitView.scrollView addObserver: self
                                 forKeyPath: @"contentSize"
                                    options: NSKeyValueObservingOptionNew
                                    context: nil];
    }

    - (void) dealloc
    {
        // remove observer
        [self.webKitView.scrollView removeObserver: self
                                        forKeyPath: @"contentSize"];
    }

    - (void) observeValueForKeyPath: (NSString*) keyPath
                           ofObject: (id) object
                             change: (NSDictionary<NSKeyValueChangeKey,id>*) change
                            context: (void*) context
    {
        if ([keyPath isEqualToString: @"contentSize"])
        {
            UIScrollView*   scroller    =   (id) object;
            CGSize          scrollFrame =   scroller.contentSize;

            NSLog(@"scrollFrame = {%@,%@}",
                  @(scrollFrame.width), @(scrollFrame.height));
        }
    }

注意 contentSize:它经常被触发。如果您的 webView 嵌入到另一个滚动区域(例如,如果您将其用作表单元素),那么当您滚动整个视图时,该子视图 webView 将触发相同值的更改。因此,请确保不要不必要地调整大小或导致不必要的刷新。

此解决方案在 iOS 12 iPad Air 2 sim off High Sierra XCode 10.

上进行了测试

您可以将 JavaScript 注入 Web 视图以等待 onDOMContentLoaded 或检查 document.readyState 状态。我有一个多年来一直这样做的应用程序,这是等待 DOM 被填充的唯一可靠方法。如果您需要加载所有图像和其他资源,则需要使用 JavaScript 等待加载事件。这里有一些文档:

https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState