How to take screenshot of portion of UIView? How to take screenshot of portion of UIView? swift swift

How to take screenshot of portion of UIView?


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.


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....