亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS使用WebView生成長截圖的第3種解決方案

2019-10-21 18:39:41
字體:
來源:轉載
供稿:網友

前言

WebView就是一個內嵌瀏覽器控件,在iOS中主要有兩種WebView:UIWebView和WKWebView,UIWebView是iOS2之后開始使用,WKWebView是在iOS8開始使用,WKWebView將逐步取代笨重的UIWebView。

由于項目需要,新近實現了一個長截圖庫 SnapshotKit。其中,需要支持 UIWebView、WKWebView 組件生成長截圖。為了實現這個特性,查閱了很多資料,同時也做了不同的新奇思路嘗試,最終實現了一個新的、取巧的技術方案。

以下主要總結了在“WebView生成長截圖”需求方面,“網上已有方案”和“我的全新方案”的各自實現要點和優缺點。

WebView生成長截圖的已有方案

根據 Google 所搜索到的資料,目前iOS WebView生成長截圖的方案主要有2種:

  • 方案一:修改Frame,截圖組件
  • 方案二:分頁截圖組件內容,合成長圖

下面將會簡述方案一和方案二的具體實現。

方案一:修改Frame,截圖組件

方案一的實現要點在于:修改 webView.scrollView 的 frameSize  為 contentSize,然后對整個 webView.scrollView 進行截圖。

不過,這個方案只適用 UIWebView 組件,因為其是一次性加載網頁所有的內容。而 WKWebView 組件,為了節省內存,加載網頁內容時,只加載可視部分——這一點類似 UITableView 組件。在修改webView.scrollView 的 frameSize 后,立即執行了截圖操作, 這時候,WKWebView由于還沒把網頁的內容加載出來,導致生成的長截圖是空白的。

方案一核心代碼如下:

extension UIScrollView { public func takeSnapshotOfFullContent() -> UIImage? {  let originalFrame = self.frame  let originalOffset = self.contentOffset  self.frame = CGRect.init(origin: originalFrame.origin, size: self.contentSize)  self.contentOffset = .zero  let backgroundColor = self.backgroundColor ?? UIColor.white  UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0)  guard let context = UIGraphicsGetCurrentContext() else {   return nil  }  context.setFillColor(backgroundColor.cgColor)  context.setStrokeColor(backgroundColor.cgColor)  self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)  let image = UIGraphicsGetImageFromCurrentImageContext()  UIGraphicsEndImageContext()  self.frame = originalFrame  self.contentOffset = originalOffset  return image }}

測試代碼:

// example code private func takeSnapshotOfUIWebView() { let image = self.webView.scrollView.takeSnapshotOfFullContent() // 處理image} 

方案二:分頁截圖組件內容,合成長圖

方案二的實現要點在于:分頁滾動WebView組件的內容,然后生成分頁截圖,最后把所有分頁截圖合成一張長圖。

這個方案適用于 UIWebView 組件和 WKWebView 組件。

方案二核心代碼如下:

extension UIScrollView { public func takeScreenshotOfFullContent(_ completion: @escaping ((UIImage?) -> Void)) {  // 分頁繪制內容到ImageContext  let originalOffset = self.contentOffset  // 當contentSize.height<bounds.height時,保證至少有1頁的內容繪制  var pageNum = 1  if self.contentSize.height > self.bounds.height {   pageNum = Int(floorf(Float(self.contentSize.height / self.bounds.height)))  }  let backgroundColor = self.backgroundColor ?? UIColor.white  UIGraphicsBeginImageContextWithOptions(self.contentSize, true, 0)  guard let context = UIGraphicsGetCurrentContext() else {   completion(nil)   return  }  context.setFillColor(backgroundColor.cgColor)  context.setStrokeColor(backgroundColor.cgColor)  self.drawScreenshotOfPageContent(0, maxIndex: pageNum) {   let image = UIGraphicsGetImageFromCurrentImageContext()   UIGraphicsEndImageContext()   self.contentOffset = originalOffset   completion(image)  } } fileprivate func drawScreenshotOfPageContent(_ index: Int, maxIndex: Int, completion: @escaping () -> Void) {  self.setContentOffset(CGPoint(x: 0, y: CGFloat(index) * self.frame.size.height), animated: false)  let pageFrame = CGRect(x: 0, y: CGFloat(index) * self.frame.size.height, width: self.bounds.size.width, height: self.bounds.size.height)  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {   self.drawHierarchy(in: pageFrame, afterScreenUpdates: true)   if index < maxIndex {    self.drawScreenshotOfPageContent(index + 1, maxIndex: maxIndex, completion: completion)   }else{    completion()   }  } }}

測試代碼:

// example codeprivate func takeSnapshotOfUIWebView() { self.uiWebView.scrollView.takeScreenshotOfFullContent { (image) in  // 處理image }}private func takeSnapshotOfWKWebView() { self.wkWebView.scrollView.takeScreenshotOfFullContent { (image) in  // 處理image }}

WebView生成長截圖的新方案

除了方案一和方案二,還有新方案嗎?

答案是肯定加確定以及一定的。

這個新方案的要點在于:iOS系統的WebView打印功能。

iOS系統支持把WebView的內容打印到PDF文件上,借助這個特性,新方案的設計如下:

  • 把 WebView組件的內容全部打印到一頁PDF上
  • 把PDF轉換成圖片

新方案的核心代碼如下:

import UIKitimport WebKit/// WebViewPrintPageRenderer: use to print the full content of webview into one imageinternal final class WebViewPrintPageRenderer: UIPrintPageRenderer { private var formatter: UIPrintFormatter private var contentSize: CGSize /// 生成PrintPageRenderer實例 /// /// - Parameters: /// - formatter: WebView的viewPrintFormatter /// - contentSize: WebView的ContentSize required init(formatter: UIPrintFormatter, contentSize: CGSize) {  self.formatter = formatter  self.contentSize = contentSize  super.init()  self.addPrintFormatter(formatter, startingAtPageAt: 0) } override var paperRect: CGRect {  return CGRect.init(origin: .zero, size: contentSize) } override var printableRect: CGRect {  return CGRect.init(origin: .zero, size: contentSize) } private func printContentToPDFPage() -> CGPDFPage? {  let data = NSMutableData()  UIGraphicsBeginPDFContextToData(data, self.paperRect, nil)  self.prepare(forDrawingPages: NSMakeRange(0, 1))  let bounds = UIGraphicsGetPDFContextBounds()  UIGraphicsBeginPDFPage()  self.drawPage(at: 0, in: bounds)  UIGraphicsEndPDFContext()  let cfData = data as CFData  guard let provider = CGDataProvider.init(data: cfData) else {   return nil  }  let pdfDocument = CGPDFDocument.init(provider)  let pdfPage = pdfDocument?.page(at: 1)  return pdfPage } private func covertPDFPageToImage(_ pdfPage: CGPDFPage) -> UIImage? {  let pageRect = pdfPage.getBoxRect(.trimBox)  let contentSize = CGSize.init(width: floor(pageRect.size.width), height: floor(pageRect.size.height))  // usually you want UIGraphicsBeginImageContextWithOptions last parameter to be 0.0 as this will us the device's scale  UIGraphicsBeginImageContextWithOptions(contentSize, true, 2.0)  guard let context = UIGraphicsGetCurrentContext() else {   return nil  }  context.setFillColor(UIColor.white.cgColor)  context.setStrokeColor(UIColor.white.cgColor)  context.fill(pageRect)  context.saveGState()  context.translateBy(x: 0, y: contentSize.height)  context.scaleBy(x: 1.0, y: -1.0)  context.interpolationQuality = .low  context.setRenderingIntent(.defaultIntent)  context.drawPDFPage(pdfPage)  context.restoreGState()  let image = UIGraphicsGetImageFromCurrentImageContext()  UIGraphicsEndImageContext()  return image } /// print the full content of webview into one image /// /// - Important: if the size of content is very large, then the size of image will be also very large /// - Returns: UIImage? internal func printContentToImage() -> UIImage? {  guard let pdfPage = self.printContentToPDFPage() else {   return nil  }  let image = self.covertPDFPageToImage(pdfPage)  return image }}extension UIWebView { public func takeScreenshotOfFullContent(_ completion: @escaping ((UIImage?) -> Void)) {  self.scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {   let renderer = WebViewPrintPageRenderer.init(formatter: self.viewPrintFormatter(), contentSize: self.scrollView.contentSize)   let image = renderer.printContentToImage()   completion(image)  } }}extension WKWebView { public func takeScreenshotOfFullContent(_ completion: @escaping ((UIImage?) -> Void)) {  self.scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {   let renderer = WebViewPrintPageRenderer.init(formatter: self.viewPrintFormatter(), contentSize: self.scrollView.contentSize)   let image = renderer.printContentToImage()   completion(image)  } }}

WebViewPrintPageRenderer 是該方案的核心類,負責把 WebView組件內容打印到PDF,然后把PDF轉換為圖片。
UIWebView 和 WKWebView 則實現對應的擴展。

測試代碼:

// example codeprivate func takeSnapshotOfUIWebView() { self.uiWebView.scrollView.takeScreenshotOfFullContent { (image) in  // 處理image }}private func takeSnapshotOfWKWebView() { self.wkWebView.scrollView.takeScreenshotOfFullContent { (image) in  // 處理image }}

三種技術方案優劣對比

那么,這三種技術方案各自存在什么優缺點呢,適用什么場景呢?

方案一:只適用 UIWebView;若網頁內容很多,生成長截圖時,會占用過多內存。 所以,該方案只適合不需要支持 WKWebView, 且網頁內容不會太多的場景。

方案二:適用 UIWebView 和 WKWebView,且特別適合 WKWebView。由于采用分頁生成截圖機制,有效減少內存占用。不過,這個方案存在一個問題:若網頁存在 position: fixed 的元素(如網頁頭部固定的導航欄),該元素會重復出現在生成的長圖上。

方案三:適用 UIWebView 和 WKWebView。其中最重要的一步——“把WebView內容打印到PDF” 是由iOS系統實現,所以該方案的性能在理論上是可以得到保障的。不過,這個方案存在一個問題:在把網頁內容打印到PDF時,iOS系統獲取的 contentSize 比WebView的實際contentSize 要大,從而導致生成的圖片在靠近底部的內容部分和實際存在一點差異。具體可以下載運行我的長截圖庫 SnapshotKit 的 Demo,通過其中的 UIWebView 和 WKWebView 截圖示例查看具體截圖效果。

以上三個方案,總的來說,解決了部分場景的需求,但都不夠完美,仍需做進一步的優化。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到IOS開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲va国产va天堂va久久| 川上优av一区二区线观看| 欧美精品电影在线| 久久亚洲电影天堂| 亚洲成人在线网| 国产丝袜高跟一区| 国产成人免费91av在线| 色综合老司机第九色激情| 奇米4444一区二区三区| 欧美精品videosex牲欧美| 亚洲成色777777女色窝| 精品日韩美女的视频高清| 亚洲女人被黑人巨大进入al| 欧美激情国产日韩精品一区18| 国产精品久久久久国产a级| 韩曰欧美视频免费观看| 97热精品视频官网| 日韩高清免费在线| 中文字幕一精品亚洲无线一区| 欧美成人精品激情在线观看| 91免费人成网站在线观看18| 国产精品流白浆视频| 欧美亚洲视频在线观看| 成人国产精品一区| 5566日本婷婷色中文字幕97| 久久人人看视频| 欧美日韩一区二区三区在线免费观看| 亚洲精品白浆高清久久久久久| 日韩风俗一区 二区| 青青草成人在线| 日韩欧美a级成人黄色| 91免费在线视频网站| 亚洲成人精品视频在线观看| 国产精品久久久| 亚洲国产精品人久久电影| 欧美激情在线有限公司| 亚洲欧美国产精品专区久久| 久久香蕉国产线看观看av| 久久久久久久久久久国产| 欧美日韩中文字幕在线视频| 亚洲女人被黑人巨大进入| 亚洲韩国日本中文字幕| 91欧美精品成人综合在线观看| 亚洲精品动漫久久久久| 国产一区二区三区在线| 国产一区二区三区精品久久久| 久久精品久久久久久国产 免费| 日韩欧美中文字幕在线播放| 国产精品免费久久久| **欧美日韩vr在线| www.欧美三级电影.com| 亚洲a在线播放| 日韩国产欧美精品在线| 日韩中文字幕视频在线观看| 国产激情久久久久| 国产ts人妖一区二区三区| 欧美精品videos性欧美| 精品中文字幕视频| 在线观看日韩www视频免费| 欧美日韩xxx| 精品人伦一区二区三区蜜桃网站| 亚洲精品资源美女情侣酒店| 亚洲无亚洲人成网站77777| 国产日韩av在线| 丝袜美腿精品国产二区| 最近2019年好看中文字幕视频| 午夜精品免费视频| 欧美高清视频一区二区| 欧美精品一本久久男人的天堂| 久久久午夜视频| 97国产在线观看| 青青久久av北条麻妃黑人| 韩国v欧美v日本v亚洲| 亚洲国产黄色片| 性金发美女69hd大尺寸| www欧美xxxx| 久久久久久尹人网香蕉| 日韩精品久久久久久久玫瑰园| 亚洲天堂日韩电影| 97在线免费观看视频| 欧美日韩亚洲国产一区| 国产综合色香蕉精品| 精品欧美aⅴ在线网站| 精品亚洲国产成av人片传媒| 69久久夜色精品国产69乱青草| 亚洲精品资源美女情侣酒店| 亚洲美女性生活视频| 欧美一级在线亚洲天堂| 91av视频在线播放| 69av在线播放| 日本在线观看天堂男亚洲| 91精品国产91久久久久久最新| 欧美大片网站在线观看| 2020久久国产精品| 久久伊人精品一区二区三区| 91精品视频免费看| 久久精品国产69国产精品亚洲| 亚洲午夜未删减在线观看| 在线免费观看羞羞视频一区二区| 亚洲高清一区二| 韩国美女主播一区| 91av视频在线观看| 欧美午夜视频在线观看| 91麻豆国产语对白在线观看| 亚洲电影天堂av| 欧美另类暴力丝袜| 日韩在线激情视频| 97精品国产97久久久久久春色| 成人精品视频久久久久| 国产精品露脸av在线| 国产日韩欧美一二三区| 国产成人中文字幕| 黄网站色欧美视频| 欧美大片va欧美在线播放| 午夜精品久久久久久久男人的天堂| 欧美午夜片欧美片在线观看| 久久综合久久88| 国产精品日本精品| 亚洲bt欧美bt日本bt| 亚洲欧美一区二区三区在线| 久久在线免费观看视频| 精品久久久久久亚洲精品| 久久夜色撩人精品| 久久久久久一区二区三区| 亚洲欧美在线x视频| 亚洲乱码一区av黑人高潮| 国产精品成人在线| 中文字幕日韩在线播放| 亚洲美女精品久久| 久久九九国产精品怡红院| 久久久视频免费观看| 一本色道久久88综合亚洲精品ⅰ| 久久精品国产精品亚洲| 日本精品久久久久影院| 色综合色综合久久综合频道88| 91精品中国老女人| 亚洲摸下面视频| 国产精品一区二区av影院萌芽| 成人淫片在线看| 91精品久久久久久久久久久久久久| 欧美日韩一区二区在线| 国产主播在线一区| 国产福利精品在线| 亚洲欧洲成视频免费观看| 国产欧美日韩丝袜精品一区| 国产精品视频地址| 精品国产一区二区在线| 成人在线观看视频网站| 中文字幕精品—区二区| 欧美裸体xxxx| 欧美一性一乱一交一视频| 国产视频一区在线| 欧美国产日韩一区二区| 黄色成人av在线| 日本久久久a级免费| 菠萝蜜影院一区二区免费| 91国产精品视频在线| 韩国三级电影久久久久久| 51色欧美片视频在线观看| 91精品视频播放| 久久亚洲春色中文字幕| 在线播放日韩欧美| 国产91对白在线播放| 国内精久久久久久久久久人|