用WKWebView 截取整個Html頁面


以前使用UIWebview時,想截取整個頁面,可以調整內部scrollView的frame,之后調用 scrollView的layer的 render 方法,很方便。

但是在WKWebView上,行不通。

我覺得以前的UIWebview其實是把整個頁面都渲染在內存中,只是我們看不到。而WKWebView為了優化內存,只渲染WKWebView的Frame大小的內容。

所以想用WKWebview截取整個頁面,必須放大WKWebview的frame。

 webView.frame = CGRect(x: 0, y: 0, width: webView.scrollView.frame.size.width, height: webView.scrollView.contentSize.height);

改變了frame之后,我們就可以利用scrollView.layer.render 去渲染整個頁面了。

但是這時候又出現了另一個問題:  渲染網頁是需要時間的,把webview的frame擴大后,我們不知道什么時候,系統完成了渲染。比如下面這個例子:

@IBAction func takeScreenshot(){
        
        webView.frame = CGRect(x: 0, y: 0, width: webView.scrollView.frame.size.width, height: webView.scrollView.contentSize.height);
        

            let scrollView = self.webView.scrollView
            
            UIGraphicsBeginImageContextWithOptions(self.webView.scrollView.contentSize,false, UIScreen.main.scale)
            
            scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
            
            let image = UIGraphicsGetImageFromCurrentImageContext()
            
            let pngData = UIImagePNGRepresentation(image!);
            
            
            let dstPath = NSHomeDirectory()+"/Documents/test.png"
            let dstUrl = URL(fileURLWithPath: dstPath)
            do{
                try  pngData?.write(to: dstUrl, options: .atomicWrite)
            }catch{
                
                
            }
            
            print("dest is %@",dstUrl);
            
            
            UIGraphicsEndImageContext()
    }
    

由於立即調用了截圖函數,webView沒有足夠的時間渲染,只多渲染了一小部分。

之后,我用下面的代碼進行測試,注意,這里延時了0.3s,給了webview一定的渲染時間:

    @IBAction func takeScreenshot(){
        
        webView.frame = CGRect(x: 0, y: 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.contentSize,false, UIScreen.main.scale)
            
            scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
            
            let image = UIGraphicsGetImageFromCurrentImageContext()
            
            let pngData = UIImagePNGRepresentation(image!);
            
            
            let dstPath = NSHomeDirectory()+"/Documents/test.png"
            let dstUrl = URL(fileURLWithPath: dstPath)
            do{
                try  pngData?.write(to: dstUrl, options: .atomicWrite)
            }catch{
                
                
            }
            
            print("dest is %@",dstUrl);
            
            
            UIGraphicsEndImageContext()
       }
        
    }
    

下面是結果的截圖,一切正常:

 

那么,如果網頁更長,0.3秒一定也不夠用,我們怎么知道該延時多少呢?

這時候我又發現了一個函數,是屬於UIView的,drawHierarchy,根據api描述,第二個參數好像和渲染有關,能不能解決我們的問題呢,繼續測試:

 @IBAction func takeScreenshot(){
        
        webView.frame = CGRect(x: 0, y: 0, width: webView.scrollView.frame.size.width, height: webView.scrollView.contentSize.height);
        
   
            let scrollView = self.webView.scrollView
            
            UIGraphicsBeginImageContextWithOptions(self.webView.scrollView.contentSize,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{
                try  pngData?.write(to: dstUrl, options: .atomicWrite)
            }catch{
                
                
            }
            
            print("dest is %@",dstUrl);
            
            
            UIGraphicsEndImageContext()
       
        
    }

結果還是不行,效果和使用layer的render方法一樣!看來afterScreenUpdates這個參數跟網頁的渲染無關了。

那么把Webview frame直接擴大為html內容的大小並截圖的方式其實是很有問題的,截圖時機不好掌握, 內存和cpu的占用也會很大。

這里要推薦一個github上的項目,https://github.com/startry/SwViewCapture, 它的解決思路如下:

1. 截圖時機的掌握:每次通過調整視圖frame,只渲染一屏的截圖,速度很快,只需稍為延遲,即可保證完美截圖。

2.內存和cpu:由於每次只處理一屏幕的截圖,內容很少,對cpu和內存的沖擊都很小。

下面貼出其中的關鍵代碼:

 fileprivate func swContentPageDraw (_ targetView: UIView, index: Int, maxIndex: Int, drawCallback: @escaping () -> Void) {
        
        // set up split frame of super view
        let 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
        var myFrame = 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)) { () -> Void in
            
            targetView.drawHierarchy(in: splitFrame, afterScreenUpdates: true)
            
            if index < maxIndex {
                self.swContentPageDraw(targetView, index: index + 1, maxIndex: maxIndex, drawCallback: drawCallback)
            }else{
                drawCallback()
            }
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM