介紹
對 UIScrollView
及其子類 UITableView
征绎、UICollectionView
的內(nèi)容進行長截圖。
思路
對 UIScrollView
進行長截屏時是整,需要知道它的全部內(nèi)容,包括未加載未渲染的內(nèi)容。
做法是通過設(shè)置它的內(nèi)容偏移量 contentOffset
挺尾, 讓 ScrollView
的內(nèi)容滑動到底部返顺,這樣便可以觸發(fā) ScrollView
中全部內(nèi)容的加載和渲染肮蛹。
用 CoreGraphics
對 ScrollView
的內(nèi)容渲染生成圖片,此時我們需要令 ScrollView
的寬高等于 它 contentSize
的真實寬高创南,在截屏結(jié)束后再恢復伦忠,因此需要提前緩存一份 ScrollView
的屬性。
問題
Q:設(shè)置 ScrollView
偏移量時稿辙,用戶看見的界面內(nèi)容會發(fā)生偏移昆码,怎么處理?
A:在設(shè)置偏移之前邻储,使用 UIView
的 func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView?
方法對當前看見的內(nèi)容截屏(注意:此方法只會截屏已顯示加載出來的內(nèi)容赋咽,非全部),然后用 addSubview()
添加 Scrollview.superView
上吨娜,蓋在 UIScrollView 之上脓匿,這樣用戶看見的就是一個假的不會變動的View
。
使用:
scrollView.swContentCapture { (image) in
// TODO:
}
全部代碼如下:
代碼中有詳細注釋
// Reference: https://github.com/startry/SwViewCapture
// Notes: The 'swContentCapture(_:)' method would lose constraints of UIScrollview, we need remake its constraints after screenshot.
import UIKit
public extension UIScrollView {
func swContentCapture (_ completionHandler: @escaping (_ capturedImage: UIImage?) -> Void) {
// Put a fake Cover of View
let snapShotView = self.snapshotView(afterScreenUpdates: false)
snapShotView?.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: (snapShotView?.frame.size.width)!, height: (snapShotView?.frame.size.height)!)
self.superview?.addSubview(snapShotView!)
// Backup all properties of scrollview if needed
let bakFrame = self.frame
let bakOffset = self.contentOffset
let bakSuperView = self.superview
let bakIndex = self.superview?.subviews.firstIndex(of: self)
// Scroll To Bottom show all cached view
if self.frame.size.height < self.contentSize.height {
self.contentOffset = CGPoint(x: 0, y: self.contentSize.height - self.frame.size.height)
}
self.swRenderImageView({ [weak self] (capturedImage) -> Void in
// Recover View
let strongSelf = self!
strongSelf.removeFromSuperview()
strongSelf.frame = bakFrame
strongSelf.contentOffset = bakOffset
bakSuperView?.insertSubview(strongSelf, at: bakIndex!)
snapShotView?.removeFromSuperview()
completionHandler(capturedImage)
})
}
private func swRenderImageView(_ completionHandler: @escaping (_ capturedImage: UIImage?) -> Void) {
// Due to scroll to bottom, delay to wait for contentOffset refreshing.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
// Rebuild scrollView superView and their hold relationship
let swTempRenderView = UIView(frame: CGRect(x: 0, y: 0, width: self.contentSize.width, height: self.contentSize.height))
self.removeFromSuperview()
swTempRenderView.addSubview(self)
self.contentOffset = CGPoint.zero
self.frame = swTempRenderView.bounds
// Sometimes ScrollView will Capture nothing without defer;
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
let bounds = self.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
completionHandler(capturedImage)
}
}
}
}