I'm trying to capture a screenshot of a WKWebView but my method doesn't work properly, it returns a solid color as if the layer tree was empty, while it seems to work on other views.
- (UIImage *)screenshot
{
UIImage *screenshot;
UIGraphicsBeginImageContext(self.frame.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenshot;
}
This Stackoverflow answer solved it for me on iOS 8 with a WKWebView where [view snapshotViewAfterScreenUpdates:]
and [view.layer renderInContext:]
either returned solid black or white:
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage* uiImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
There's still a bug with the WKWebView but you can work your way around it by using another function:
[webView snapshotViewAfterScreenUpdates:NO or YES];
It works.
The drawViewHierarchyInRect method will work, but will cause the screen to briefly flash.
If you need high performance (capturing many frames quickly), I suggest adding the WKWebView to your window somewhere (even if you push it out of the visible area with the frame offset or make it transparent). I found that renderInContext is much faster, and works fine as long as the view is in the view hierarchy of the window.
Please refer to this answer
iOS 11.0 and above, Apple has provided following API to capture snapshot of WKWebView.
@available(iOS 11.0, *)
open func takeSnapshot(with snapshotConfiguration: WKSnapshotConfiguration?, completionHandler: @escaping (UIImage?, Error?) -> Swift.Void)
Sample usage:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if #available(iOS 11.0, *) {
webView.takeSnapshot(with: nil) { (image, error) in
//Do your stuff with image
}
}
}
Here are my two points. Before the iOS 10.3.1 renderInContext
used to work well for the WKWebView. But only if a WKWebView is included in a presented view hierarchy. In other words, the system is drawing it on the screen.
In the iOS 10.3.1, renderInContext
doesn't work for me. In the same moment, drawViewHierarchyInRect
works fine in the iOS 10.3.1. But only if it's in the view hierarchy! Even worse. When I was trying to get a snapshot of the WKWebView, which wasn't presented on the current screen, the original view became invalidated. Thus taking screenshot can break a WKWebView.
Here is my workaround.
- I save a snapshot (
UIImage
) with drawViewHierarchyInRect
every time when I need it. But only if the WKWebView is on the screen.
- If the WKWebView isn't in the view hierarchy I use a saved snapshot.
Try this
- (IBAction)takeScreenShot:(id)sender {
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
CGRect rect = [keyWindow bounds];
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[keyWindow.layer renderInContext:context];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(img, self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo
{
// Was there an error?
if (error != NULL)
{
// Show error message...
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"Image not Saved" message:@"" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alertView show];
}
else // No errors
{
// Show message image successfully saved
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"Image Saved" message:@"This chart has been save to your photos" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alertView show];
}
}