需求说明
1、可以将整个网页进行截屏,不止单屏幕。
2、限制的高度为6000单位。不限制的话占用内存太高。
核心思路与代码
先截当前页面图片盖住webview,做成webview没动的假象,
记录初始偏移量,截图完毕后恢复。
然后开启一个绘图上下文,截取一屏后滚动到下一屏截图。
整个web截完或者超出6000单位则停止截图,生产image。
//MARK: webSnapShot
func webSnapShot(_ completionHandler: @escaping (_ capturedImage: UIImage?) -> Void) {
guard let webview = self.tabWebView?.webView else {
return
}
let offset = webview.scrollView.contentOffset
guard let snapShotView = self.snapshotView(afterScreenUpdates: true) else {
print("截图失败")
return
}
snapShotView.frame = self.frame
self.superview?.addSubview(snapShotView)
if webview.frame.size.height < webview.scrollView.contentSize.height {
webview.scrollView.contentOffset = CGPoint(x: 0, y: webview.scrollView.contentSize.height - webview.frame.size.height)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0.3 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { () -> Void in
if offset.y > 6000.0 {
webview.scrollView.contentOffset = CGPoint(x: 0, y: offset.y - 6000.0 + self.height)
} else {
webview.scrollView.contentOffset = CGPoint.zero
}
self.webSnapShotWithOffSet({ (capturedImage) -> Void in
webview.scrollView.contentOffset = offset
webview.layoutIfNeeded()
snapShotView.removeFromSuperview()
completionHandler(capturedImage)
})
}
}
fileprivate func webSnapShotWithOffSet(_ completionHandler: @escaping (_ capturedImage: UIImage?) -> Void) {
guard let webview = self.tabWebView?.webView else {
return
}
var totalSize = webview.scrollView.contentSize
if webview.scrollView.contentSize.height > 6000.0 {
totalSize = CGSize(width: self.width, height: 6000.0)
}
let page = floorf(Float(totalSize.height / self.bounds.height))
webview.snp.removeConstraints()
webview.snp.makeConstraints({ (make) in
make.leading.right.equalToSuperview()
make.height.equalTo(totalSize.height)
self.webViewTopConstraint = make.top.equalTo(0).constraint
})
UIGraphicsBeginImageContextWithOptions(totalSize, false, UIScreen.main.scale)
self.ContentPageDraw(0, maxIndex: Int(page), drawCallback: { () -> Void in
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
webview.snp.removeConstraints()
webview.snp.makeConstraints({ (make) in
make.edges.equalToSuperview()
})
completionHandler(capturedImage)
})
}
fileprivate func ContentPageDraw(_ index: Int, maxIndex: Int, drawCallback: @escaping () -> Void) {
let splitFrame = CGRect(x: 0, y: CGFloat(index) * self.height, width: self.width, height: self.height)
webViewTopConstraint?.update(offset: -(CGFloat(index) * self.height))
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0.2 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { () -> Void in
self.drawHierarchy(in: splitFrame, afterScreenUpdates: true)
if index < maxIndex {
self.ContentPageDraw(index + 1, maxIndex: maxIndex, drawCallback: drawCallback)
}else {
drawCallback()
}
}
}
开始的做法会先滚到webview最上面开始截图。但是当偏移过大时,在恢复偏移量时跳跃太大导致webview被回收了。
因此添加了判断,如果webview的contentsize超过6000,则不滚到最上面开始截图,直接在初始位置开始向下截图。