在 swift 中捕获自由形式的视图控制器屏幕截图
Capture freeform view controller screenshot in swift
我想截取整个可滚动视图控制器的屏幕截图。它的高度为 1025 的自由格式视图控制器。屏幕截图图像应该是 1045 的整个屏幕或滚动视图框架。
我试过这段代码,但它只捕获可见的矩形。我要滚动视图下的整个视图的屏幕截图
fileprivate extension UIScrollView {
func screenshot() -> UIImage? {
// begin image context
UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
// save the orginal offset & frame
let savedContentOffset = contentOffset
let savedFrame = frame
// end ctx, restore offset & frame before returning
defer {
UIGraphicsEndImageContext()
contentOffset = savedContentOffset
frame = savedFrame
}
// change the offset & frame so as to include all content
contentOffset = .zero
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
您的代码中的问题是 layer.render
触发了 layoutSubviews 并重置了您的框架。
因为我们不想松开所有约束,所以我们需要存储它们,在屏幕捕获时禁用它们,然后再次打开它们:
extension UIScrollView {
func screenshot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
// save the orginal offset, take a ref to all constraints related to the view
let savedContentOffset = contentOffset
let actualConstraints = relatedConstraints()
// deactivate non needed constraints so they won't stop us from resiging scroll view
NSLayoutConstraint.deactivate(actualConstraints)
// enable auth generated constraints based on the frame
translatesAutoresizingMaskIntoConstraints = true
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
contentOffset = .zero
defer {
UIGraphicsEndImageContext()
// reset original constraints
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(actualConstraints)
// layout superview needed before resetting content offset
superview?.layoutIfNeeded()
contentOffset = savedContentOffset
}
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
extension UIView {
func relatedConstraints() -> [NSLayoutConstraint] {
var constraints = self.constraints
var parent = superview
while parent != nil {
constraints.append(contentsOf: parent!.constraints.filter { [=10=].firstItem === self || [=10=].secondItem === self })
parent = parent!.superview
}
return constraints
}
}
我想截取整个可滚动视图控制器的屏幕截图。它的高度为 1025 的自由格式视图控制器。屏幕截图图像应该是 1045 的整个屏幕或滚动视图框架。
我试过这段代码,但它只捕获可见的矩形。我要滚动视图下的整个视图的屏幕截图
fileprivate extension UIScrollView {
func screenshot() -> UIImage? {
// begin image context
UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
// save the orginal offset & frame
let savedContentOffset = contentOffset
let savedFrame = frame
// end ctx, restore offset & frame before returning
defer {
UIGraphicsEndImageContext()
contentOffset = savedContentOffset
frame = savedFrame
}
// change the offset & frame so as to include all content
contentOffset = .zero
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
您的代码中的问题是 layer.render
触发了 layoutSubviews 并重置了您的框架。
因为我们不想松开所有约束,所以我们需要存储它们,在屏幕捕获时禁用它们,然后再次打开它们:
extension UIScrollView {
func screenshot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
// save the orginal offset, take a ref to all constraints related to the view
let savedContentOffset = contentOffset
let actualConstraints = relatedConstraints()
// deactivate non needed constraints so they won't stop us from resiging scroll view
NSLayoutConstraint.deactivate(actualConstraints)
// enable auth generated constraints based on the frame
translatesAutoresizingMaskIntoConstraints = true
frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
contentOffset = .zero
defer {
UIGraphicsEndImageContext()
// reset original constraints
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(actualConstraints)
// layout superview needed before resetting content offset
superview?.layoutIfNeeded()
contentOffset = savedContentOffset
}
guard let ctx = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: ctx)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
}
extension UIView {
func relatedConstraints() -> [NSLayoutConstraint] {
var constraints = self.constraints
var parent = superview
while parent != nil {
constraints.append(contentsOf: parent!.constraints.filter { [=10=].firstItem === self || [=10=].secondItem === self })
parent = parent!.superview
}
return constraints
}
}