I can't capture the screenshot of a WKWebView

2019-04-09 18:25发布

问题:

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;
}

回答1:

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();


回答2:

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.



回答3:

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.



回答4:

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
            }
        }
    }


回答5:

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.

  1. I save a snapshot (UIImage) with drawViewHierarchyInRect every time when I need it. But only if the WKWebView is on the screen.
  2. If the WKWebView isn't in the view hierarchy I use a saved snapshot.


回答6:

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];
}
}