Search code examples
swiftmacoscocoascreenshotwkwebview

intermittent blank/partial screenshots of WKWebView using takeSnapshot


I am taking a screenshot of a WKVebView in the completion handler of a javascript evaluation:

func loadPage(){
    let fileURL = URL(fileURLWithPath: "...")
    let baseUrl = URL(fileURLWithPath: "...")
    webView?.navigationDelegate = self
    webView.loadFileURL(fileURL, allowingReadAccessTo: baseUrl)
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("....") { (result, error) in
        let configuration = WKSnapshotConfiguration()
        configuration.rect = CGRect(origin: .zero, size: (self.webView?.frame.size)!)
        self.webView!.takeSnapshot(with: configuration, completionHandler: { (image, error) in
            // do something with the image
        })
}

Even if it is in the completion handler of the webView.evaluateJavaScript function, the content if often not finished to render and I am getting either blank screenshot or partial ones (only some elements are present).

How can I ensure that rendering is completed before taking screenshot (I do not want to introduce fixed delay)


Solution

  • As content of WebView is bigger than the screen size so first we will get height and width of the content after that we will take a screenshot

      func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    
        // Will get Height and Width of the Content
    
        self.webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
            if complete != nil {
                self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
                    guard let contentHeight = height as? CGFloat else { print("Content height could not be obtained"); return }
                    self.webView.evaluateJavaScript("document.body.scrollWidth", completionHandler: { [weak self](width, error) in
                        let contentWidth = width as! CGFloat
                        let rect = CGRect(x: 0, y: 0, width: contentWidth, height: contentHeight)
                        self?.takeScreenshot(rect)
                    })
                })
            }
        })
    
    }
    

    Once we will get the Height and Width, we will call below method to capture Screenshot

    func takeScreenshot(_ rect: CGRect) {
        webView.evaluateJavaScript(".....") { (result, error) in
            let configuration = WKSnapshotConfiguration()
            configuration.rect = rect//CGRect(origin: .zero, size: (self.webView?.frame.size)!)
            self.webView!.takeSnapshot(with: configuration, completionHandler: { (image, error) in
                // do something with the image
                print("image:\(image!)")
            })
        }
    }
    

    Hope it will help you.