欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2018年09月23日 13:00:43   作者:York_魚  
這篇文章主要給大家介紹了關(guān)于iOS使用WebView生成長截圖的第3種解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

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

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

以下主要總結(jié)了在“WebView生成長截圖”需求方面,“網(wǎng)上已有方案”和“我的全新方案”的各自實(shí)現(xiàn)要點(diǎn)和優(yōu)缺點(diǎn)。

WebView生成長截圖的已有方案

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

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

下面將會簡述方案一和方案二的具體實(shí)現(xiàn)。

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

方案一的實(shí)現(xiàn)要點(diǎn)在于:修改 webView.scrollView 的 frameSize  為 contentSize,然后對整個(gè) webView.scrollView 進(jìn)行截圖。

不過,這個(gè)方案只適用 UIWebView 組件,因?yàn)槠涫且淮涡约虞d網(wǎng)頁所有的內(nèi)容。而 WKWebView 組件,為了節(jié)省內(nèi)存,加載網(wǎng)頁內(nèi)容時(shí),只加載可視部分——這一點(diǎn)類似 UITableView 組件。在修改webView.scrollView 的 frameSize 后,立即執(zhí)行了截圖操作, 這時(shí)候,WKWebView由于還沒把網(wǎng)頁的內(nèi)容加載出來,導(dǎo)致生成的長截圖是空白的。

方案一核心代碼如下:

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
} 

方案二:分頁截圖組件內(nèi)容,合成長圖

方案二的實(shí)現(xiàn)要點(diǎn)在于:分頁滾動(dòng)WebView組件的內(nèi)容,然后生成分頁截圖,最后把所有分頁截圖合成一張長圖。

這個(gè)方案適用于 UIWebView 組件和 WKWebView 組件。

方案二核心代碼如下:

extension UIScrollView {
 public func takeScreenshotOfFullContent(_ completion: @escaping ((UIImage?) -> Void)) {
  // 分頁繪制內(nèi)容到ImageContext
  let originalOffset = self.contentOffset

  // 當(dāng)contentSize.height<bounds.height時(shí),保證至少有1頁的內(nèi)容繪制
  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 code
private func takeSnapshotOfUIWebView() {
 self.uiWebView.scrollView.takeScreenshotOfFullContent { (image) in
  // 處理image
 }
}

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

WebView生成長截圖的新方案

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

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

這個(gè)新方案的要點(diǎn)在于:iOS系統(tǒng)的WebView打印功能。

iOS系統(tǒng)支持把WebView的內(nèi)容打印到PDF文件上,借助這個(gè)特性,新方案的設(shè)計(jì)如下:

  • 把 WebView組件的內(nèi)容全部打印到一頁P(yáng)DF上
  • 把PDF轉(zhuǎn)換成圖片

新方案的核心代碼如下:

import UIKit
import WebKit

/// WebViewPrintPageRenderer: use to print the full content of webview into one image
internal final class WebViewPrintPageRenderer: UIPrintPageRenderer {

 private var formatter: UIPrintFormatter

 private var contentSize: CGSize

 /// 生成PrintPageRenderer實(shí)例
 ///
 /// - 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 是該方案的核心類,負(fù)責(zé)把 WebView組件內(nèi)容打印到PDF,然后把PDF轉(zhuǎn)換為圖片。
UIWebView 和 WKWebView 則實(shí)現(xiàn)對應(yīng)的擴(kuò)展。

測試代碼:

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

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

三種技術(shù)方案優(yōu)劣對比

那么,這三種技術(shù)方案各自存在什么優(yōu)缺點(diǎn)呢,適用什么場景呢?

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

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

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

以上三個(gè)方案,總的來說,解決了部分場景的需求,但都不夠完美,仍需做進(jìn)一步的優(yōu)化。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • iOS設(shè)置可選擇圓角方向的控件圓角

    iOS設(shè)置可選擇圓角方向的控件圓角

    在iOS開發(fā)中會遇到設(shè)置控件圓角的效果,這篇文章就給大家分享了實(shí)現(xiàn)的方法,且可以選擇圓角的方向,有需要的朋友們可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2016-11-11
  • IOS 單擊手勢的添加實(shí)現(xiàn)代碼

    IOS 單擊手勢的添加實(shí)現(xiàn)代碼

    這篇文章主要介紹了IOS 單擊手勢的添加實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • iOS實(shí)現(xiàn)裁剪框和圖片剪裁功能

    iOS實(shí)現(xiàn)裁剪框和圖片剪裁功能

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)裁剪框和圖片剪裁功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • IOS 簽名錯(cuò)誤codesign failed with exit code 1解決方法

    IOS 簽名錯(cuò)誤codesign failed with exit code 1解決方法

    這篇文章主要介紹了IOS 簽名錯(cuò)誤codesign failed with exit code 1解決方法的相關(guān)資料,遇到同樣問題的朋友可以看下,這里提供了解決方案,需要的朋友可以參考下
    2017-01-01
  • iOS自定義鍵盤切換效果

    iOS自定義鍵盤切換效果

    這篇文章主要為大家詳細(xì)介紹了iOS自定義鍵盤切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • CocoaPods1.9.0 安裝使用教程詳解

    CocoaPods1.9.0 安裝使用教程詳解

    CocoaPods是OS X和iOS下的一個(gè)第三類庫管理工具,這篇文章主要介紹了CocoaPods1.9.0 安裝使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • iOS實(shí)現(xiàn)雙向滑動(dòng)條效果

    iOS實(shí)現(xiàn)雙向滑動(dòng)條效果

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)雙向滑動(dòng)條效果的相關(guān)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • ios基于UITableViewController實(shí)現(xiàn)列表

    ios基于UITableViewController實(shí)現(xiàn)列表

    這篇文章主要介紹了ios基于UITableViewController實(shí)現(xiàn)列表的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • IOS筆記061之二維碼的生成和掃描

    IOS筆記061之二維碼的生成和掃描

    隨著移動(dòng)設(shè)備的普及為二維碼提供了一個(gè)很好應(yīng)用平臺,無論是在商城購物還是美食城都離不開二維碼,本篇文章就給大家介紹IOS筆記061之二維碼的生成和掃描,感興趣的朋友可以過來一起學(xué)習(xí)啦,本文內(nèi)容講的很詳細(xì)
    2015-08-08
  • iOS開發(fā)中蘋果輸入手機(jī)號變用戶的名字

    iOS開發(fā)中蘋果輸入手機(jī)號變用戶的名字

    今天我們的用戶輸入手機(jī)號之后變成了用戶的名字,沒辦法獲取驗(yàn)證碼,因?yàn)槭謾C(jī)格式不對。下面通過本文給大家分享開發(fā)中蘋果輸入手機(jī)號變用戶的名字,需要的朋友可以參考下
    2017-05-05

最新評論