I want to take a screenshot of a WKWebView
. I call the method drawViewHierarchyInRect
(with screen updates set to true
) right after th
as I've just worked on this, let me know what I tried and how I figured it out. I tried a couple of things, first, a WKUserScript
let webViewUserScript = WKUserScript(
source: "window.onload = function() { webkit.messageHandlers.status.postMessage(\(wkwebView.hashValue)) }",
injectionTime: WKUserScriptInjectionTime.AtDocumentEnd,
forMainFrameOnly: true
)
on the "status" namespace. This theoretically waits for all contents to be loaded, and then calls the WKScriptMessageHandler
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage){ }
when registered via
wkwebView.configuration.userContentController.addScriptMessageHandler(self, name: "status")
where I thought everything would be ready. Worked sometimes, but not always.
As you, I've also tried adding a WKNavigationDelegate and using
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) { }
Again, this didn't work. Also, adding an observer for the estimatedProgress value
wkwebView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)
and then trying to make the screenshot within
override func observeValueForKeyPath(keyPath: String?, ofObject: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { }
didn't work.
However, what does work is to check upon the webView's content size regularly. I've did this within the userContentController function, which is called by the WKUserScript - but you can also do this within the didFinishNavigation function, and then simply change the function which is to be called again. Here the code:
let _contentSize = webview.scrollView.contentSize;
if (_contentSize.height == 0){
dispatch_after(createDispatchTime(0.01), dispatch_get_main_queue()) {
self.userContentController(userContentController, didReceiveScriptMessage: message)
}
return
}
This essentially checks on the contentSize height property and if it's zero, it will call the method again every 0.01 seconds until the property is set. Here are the functions I used for creating the screenshot
var view : UIView!
if self.useSnapShot {
// Use the snapshot function
view = webView.snapshotViewAfterScreenUpdates(true)
} else {
// Draw the view as UIImage
UIGraphicsBeginImageContextWithOptions(webView.frame.size, false, 0.0)
webView.drawViewHierarchyInRect(webView.frame, afterScreenUpdates: true)
let snapshot = UIGraphicsGetImageFromCurrentImageContext()
view = UIImageView(image: snapshot)
view.frame = webView.frame
}
I've tested it on both device and simulator and it works.
Edit
Here the function for creating the dispatch time
private func createDispatchTime(seconds: Double) -> dispatch_time_t{
let delay = seconds * Double(NSEC_PER_SEC)
return dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
}
Edit #2
I created a gist which shows an even better way I believe https://gist.github.com/christopherhelf/1640454bcace39a87c93#file-wkwebviewplus-swift
I replaced the setContentSize function of the webviews UIScrollview and notify the parent class that a change has been made.