Before using UIWebView, want to intercept the entire page, you can adjust the internal scrollview frame, and then call ScrollView layer of the Render method, very convenient.
But on the Wkwebview, it won't work.
I think the former UIWebView actually renders the entire page in memory, but we don't see it. Wkwebview, in order to optimize memory, renders only the contents of the Wkwebview frame size.
So you want to use Wkwebview to intercept the entire page, you must enlarge the Wkwebview frame.
0 0, Width:webView.scrollView.frame.size.width, height:webView.scrollView.contentSize.height);
After changing the frame, we can use ScrollView.layer.render to render the entire page.
But then another problem arises: it takes time to render the page, and when the webview frame is enlarged, we don't know when the system has finished rendering. For example, the following:
@IBAction func takescreenshot () {webview.frame= CGRect (x:0Y:0, Width:webView.scrollView.frame.size.width, height:webView.scrollView.contentSize.height); Let ScrollView=Self.webView.scrollView uigraphicsbeginimagecontextwithoptions (self.webView.scrollView.content Size,false, UIScreen.main.scale) ScrollView.layer.render (inch: Uigraphicsgetcurrentcontext ()!) Let image=Uigraphicsgetimagefromcurrentimagecontext () let Pngdata= Uiimagepngrepresentation (image!); Let Dstpath= Nshomedirectory () +"/documents/test.png"Let dsturl=URL (Fileurlwithpath:dstpath) Do{ TryPngdata?. Write (To:dsturl, options:. Atomicwrite)}Catch{} print ("Dest is%@", Dsturl); Uigraphicsendimagecontext ()}
Because the function is called immediately, WebView does not have enough time to render, rendering only a small portion.
After that, I used the following code to test, notice, here the delay of 0.3s, gave the webview a certain amount of rendering time:
@IBAction func takescreenshot () {webview.frame= CGRect (x:0Y:0, Width:webView.scrollView.frame.size.width, height:webView.scrollView.contentSize.height); DispatchQueue.main.asyncAfter (Deadline:DispatchTime.now () +0.3 ) {let ScrollView=Self.webView.scrollView uigraphicsbeginimagecontextwithoptions (self.webView.scrollView.content Size,false, UIScreen.main.scale) ScrollView.layer.render (inch: Uigraphicsgetcurrentcontext ()!) Let image=Uigraphicsgetimagefromcurrentimagecontext () let Pngdata= Uiimagepngrepresentation (image!); Let Dstpath= Nshomedirectory () +"/documents/test.png"Let dsturl=URL (Fileurlwithpath:dstpath) Do{ TryPngdata?. Write (To:dsturl, options:. Atomicwrite)}Catch{} print ("Dest is%@", Dsturl); Uigraphicsendimagecontext ()}}
Here are the results, everything works:
So, if the page is longer, 0.3 seconds must not be enough, how do we know how much delay?
At this time I found a function, belongs to UIView, Drawhierarchy, according to the API description, the second parameter seems to be related to rendering, can solve our problem, continue to test:
@IBAction func takescreenshot () {webview.frame= CGRect (x:0Y:0, Width:webView.scrollView.frame.size.width, height:webView.scrollView.contentSize.height); Let ScrollView=Self.webView.scrollView uigraphicsbeginimagecontextwithoptions (self.webView.scrollView.content Size,false, UIScreen.main.scale) Self.webView.drawHierarchy ( In:cgrect (x:0, y:0, width:self.webView.scrollView.frame.size.width, Height: Self.webView.scrollView.contentSize.height), Afterscreenupdates:true ) let image=Uigraphicsgetimagefromcurrentimagecontext () let Pngdata= Uiimagepngrepresentation (image!); Let Dstpath= Nshomedirectory () +"/documents/test.png"Let dsturl=URL (Fileurlwithpath:dstpath) Do{ TryPngdata?. Write (To:dsturl, options:. Atomicwrite)}Catch{} print ("Dest is%@", Dsturl); Uigraphicsendimagecontext ()}
The result is still not working, and the effect is the same as using the layer's Render Method! It seems afterscreenupdates This parameter is irrelevant to the rendering of the Web page.
Then the WebView frame directly to the size of the HTML content and the way is actually very problematic, timing is not mastered, memory and CPU occupancy will be very large.
Here to recommend a project on GitHub, Https://github.com/startry/SwViewCapture, it's the solution of the following ideas:
1. Timing: Each time by adjusting the view frame, rendering only one screen, fast, just a little delay, you can guarantee the perfect.
2. Memory and CPU: With only one screen at a time, there is little content, and the impact on both CPU and memory is small.
The key code is posted below:
Fileprivate Func Swcontentpagedraw (_ Targetview:uiview, Index:int, Maxindex:int, Drawcallback: @escaping ()Void) { //set up split frame of Super ViewLet Splitframe = CGRect (x:0, Y:cgfloat (Index) *targetView.frame.size.height, Width:targetView.bounds.size.width, Height:targetView.frame.size.height)//set up WebView frame varMyFrame =self.frame MYFRAME.ORIGIN.Y=-(cgfloat (Index) *targetView.frame.size.height) Self.frame=myframe DispatchQueue.main.asyncAfter (Deadline:DispatchTime.now ()+ Double (Int64 (0.3* Double (NSEC_PER_SEC))/double (nsec_per_sec)) {()-VoidinchTargetview.drawhierarchy (In:splitframe, afterscreenupdates:true) ifIndex <Maxindex {self. Swcontentpagedraw(TargetView, Index:index+1, Maxindex:maxindex, Drawcallback:drawcallback)}Else{drawcallback () }}}
Use Wkwebview to intercept an entire HTML page