首页 > 解决方案 > 快速捕获自由形式的视图控制器屏幕截图

问题描述

我想捕获整个可滚动视图控制器的屏幕截图。其高度为 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
}

}

标签: iosswiftuiscrollview

解决方案


您的代码中的问题是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 { $0.firstItem === self || $0.secondItem === self })
            parent = parent!.superview
        }
        return constraints
    }
}

推荐阅读