How to take screenshot of portion of UIView?

This is a previously asked question at How to capture UIView to UIImage without loss of quality on retina display but to expand in 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)
    }
}

So you can either get an image from a view with UIView.image(theView) or by asking the view itself let viewImage = self.view.image()

Do bear in mind though that this is rough and probably needs further looking at for thread safety etc....


The standard snapshot technique is drawHierarchy(in:afterScreenUpdates:), drawing that to an image context. In iOS 10 and later, you can use 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)
        }
    }
}

And you’d use that like so:

let image = webView.snapshot(of: rect)

Prior to iOS 10, you would to get portion of an image, you can use CGImage method cropping(to:). E.g.:

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

}

Which uses this little convenient operator:

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

And to use it, you can do:

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

For Swift 2 rendition, see previous revision of this answer.