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

shr*_*tim 24 objective-c webview ios swift

目标:在网站加载完成后截取WKWebView的截图

采用的方法:

  • 在UIViewController中定义了WKWebView var
  • 创建了一个名为screen capture()的扩展方法,它获取了WKWebView的图像

  • 使我的UIViewController实现WKNavigationDelegate

  • 设置wkwebview.navigationDelegate = self(在UIViewController init中)

  • 在UIViewcontroller中实现didFinishNavigation委托func,为WKWebView调用屏幕捕获扩展方法

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
    let img = webView.screenCapture()
}
Run Code Online (Sandbox Code Playgroud)

问题:

  • 当我调试i模拟器时,我注意到控件到达didFinishNavigation()函数,即使网站尚未在WKWebView中呈现
  • 相应地,拍摄的图像截图是白色斑点.

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


更新:添加我用于截取Web视图截屏的截图代码

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
 }
Run Code Online (Sandbox Code Playgroud)

San*_*h R 26

    对于那些仍在寻找答案的人来说,明显的答案是BS,他只是强迫他的方式让它被接受.
   "加载"和webView(webView:WKWebView,didFinishNavigation导航:WKNavigation!)都做同样的事情,指示主资源是否已加载.现在这并不意味着整个网页/网站都被加载,因为它实际上取决于网站的实施.如果需要加载脚本和资源(图像,字体等)使其自身可见,导航完成后仍然看不到任何内容,因为webview不会跟踪网站发出的网络调用,只有导航是跟踪,所以它不会真正知道什么时候网站完全加载.

  • 您可以让网络应用程序调用您的javascript messageHandler,让iOS应用程序知道何时加载特定资源.但是,如果它不是您的网络应用程序或无法访问其代码,那么它不太可能.您仍然可以尝试入侵js并找到观察某些事物的方法(例如,如果只有在加载图像后定义的函数,您可以检查在给定时间是否存在这将告诉您图像已加载)但是,没有直接的跟踪方式. (2认同)
  • 这应该被标记为正确的解决方案 - 资源加载是吸引所有人的事情.加载标志仅表示已加载html.这并不意味着加载了所有布局,图像和脚本.在简单的页面上,这个标志给出了误报.当"足够"的页面加载时,JS回调是唯一正确的方法. (2认同)

mat*_*att 12

当内容加载完成时,WKWebView不会使用委托来告诉您(这就是为什么您找不到适合您目的的委托方法).了解WKWebView是否仍在加载的方法是使用KVO(键值观察)来监视其loading属性.这样一来,您会收到通知时,loading从变化truefalse.

这是一个循环动画gif,显示我测试时会发生什么.我加载一个Web视图并loading通过KVO 响应其属性来拍摄快照.上部视图是Web视图; 较低(压扁)的视图是快照.如您所见,快照确实捕获了加载的内容:

在此输入图像描述

  • 在我的测试中,观察`loading`给了我与使用`WKNavigationDelegate`委托回调`webView:didFinishNavigation`相同的结果. (9认同)
  • 我尝试了KVO方法。即使在加载网站之前,“loading”键也会更改为 false。所以这对我不起作用 (2认同)

Esq*_*uth 8

这是我解决它的方式:

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

更新Swift 3.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
    }
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*oie 5

对于不完整的解决方案,很多人在这里放弃。关于“加载”的观察者并不可靠,因为当将其切换为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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

当心contentSize:它被触发了很多。如果将webView嵌入到另一个滚动区域中(例如,将其用作表单元素),则在滚动整个视图时,该子视图webView会触发相同值的更改。因此,请确保不要不必要地调整大小或引起不必要的刷新。

此解决方案已在High Sierra XCode 10的iPad Air 2 sim上的iOS 12上进行了测试。