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