在 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
    }
}