如何截取 UIView 的部分屏幕截图?

16

我希望用户在我的应用程序上按程序按钮后,能够截取应用程序的屏幕截图。我知道UIGraphicsGetImageFromCurrentImageContext()可以截屏,但我不想要整个屏幕的图片。我想让一个矩形框弹出(有点像剪裁工具),用户可以拖动和调整矩形框的大小来截取屏幕的某一部分。我希望这个矩形框覆盖在一个WKWebView上,并剪裁出WebView的图片。

3个回答

43
标准的快照技术是使用drawHierarchy(in:afterScreenUpdates:)将其绘制到图像上下文中。在iOS 10及更高版本中,您可以使用UIGraphicsImageRenderer
extension UIView {

    /// Create image snapshot of view.
    ///
    /// - Parameters:
    ///   - rect: The coordinates (in the view's own coordinate space) to be captured. If omitted, the entire `bounds` will be captured.
    ///   - afterScreenUpdates: A Boolean value that indicates whether the snapshot should be rendered after recent changes have been incorporated. Specify the value false if you want to render a snapshot in the view hierarchy’s current state, which might not include recent changes. Defaults to `true`.
    ///
    /// - Returns: The `UIImage` snapshot.

    func snapshot(of rect: CGRect? = nil, afterScreenUpdates: Bool = true) -> UIImage {
        return UIGraphicsImageRenderer(bounds: rect ?? bounds).image { _ in
            drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
        }
    }
}

你可以这样使用:

let image = webView.snapshot(of: rect)

iOS 10之前,如果要获取图像的一部分,可以使用CGImage方法cropping(to:)。例如:

extension UIView {

    /// Create snapshot
    ///
    /// - Parameters:
    ///   - rect: The coordinates (in the view's own coordinate space) to be captured. If omitted, the entire `bounds` will be captured.
    ///   - afterScreenUpdates: A Boolean value that indicates whether the snapshot should be rendered after recent changes have been incorporated. Specify the value false if you want to render a snapshot in the view hierarchy’s current state, which might not include recent changes. Defaults to `true`.
    ///
    /// - Returns: Returns `UIImage` of the specified portion of the view.

    func snapshot(of rect: CGRect? = nil, afterScreenUpdates: Bool = true) -> UIImage? {
        // snapshot entire view

        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
        drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
        let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        // if no `rect` provided, return image of whole view

        guard let image = wholeImage, let rect = rect else { return wholeImage }

        // otherwise, grab specified `rect` of image

        guard let cgImage = image.cgImage?.cropping(to: rect * image.scale) else { return nil }
        return UIImage(cgImage: cgImage, scale: image.scale, orientation: .up)
    }

}

这里使用了这个方便的运算符:

extension CGRect {
    static func * (lhs: CGRect, rhs: CGFloat) -> CGRect {
        return CGRect(x: lhs.minX * rhs, y: lhs.minY * rhs, width: lhs.width * rhs, height: lhs.height * rhs)
    }
}

而要使用它,您可以执行:

if let image = webView.snapshot(of: rect) {
    // do something with `image` here
}

对于 Swift 2 版本,请查看此答案的先前版本


1
完美的解决方案。在SO上还有其他的解决方案,但它们会捕捉整个屏幕。这是适用于两者的解决方案。 - Septronic
Rob,这太有用了!谢谢伙计。 - Roi Mulia
非常感谢Rob。经过2年的寻找,这就是我一直在寻找的解决方案! - SwiftStudier
我需要为正在播放视频的播放器捕获屏幕截图,但它无法捕获AVPlayerLayer(因为它是层?)。我正在从图层视图中获取屏幕截图。 - Ravi

3

这是之前在如何在保持视网膜显示质量的情况下捕获UIView到UIImage上提出的问题,但要在swift(2.3)中进行扩展:

extension UIView {

    class func image(view: UIView) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        view.layer.renderInContext(ctx)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }

    func image() -> UIImage? {
        return UIView.image(self)
    }
}

你可以通过 UIView.image(theView) 或直接询问视图本身 let viewImage = self.view.image() 来获取视图的图像。

请注意,这仅是初步版本,可能需要进一步考虑线程安全等问题。


0

已更新为Swift 5,并对代码进行了一些简化。

extension UIView {
var image: UIImage? {
    UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
    guard let context = UIGraphicsGetCurrentContext() else {
        return nil
    }
    layer.render(in: context)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接