How to convert a UIView to an image

IosSwiftUiviewScreenshot

Ios Problem Overview


I want to convert a UIView to an image and save it in my app. Can someone please tell me how to take screenshot of a view or convert it to an image and what is the best way to save it in an app (Not camera roll)? Here is the code for the view:

var overView   = UIView(frame: CGRectMake(0, 0, self.view.frame.width/1.3, self.view.frame.height/1.3))
overView.center = CGPointMake(CGRectGetMidX(self.view.bounds),
CGRectGetMidY(self.view.bounds)-self.view.frame.height/16);
overView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(overView)
self.view.bringSubviewToFront(overView)

Ios Solutions


Solution 1 - Ios

An extension on UIView should do the trick.

extension UIView {
    
    // Using a function since `var image` might conflict with an existing variable
    // (like on `UIImageView`)
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

Apple discourages using UIGraphicsBeginImageContext starting iOS 10 with the introduction of the P3 color gamut. UIGraphicsBeginImageContext is sRGB and 32-bit only. They introduced the new UIGraphicsImageRenderer API that is fully color managed, block-based, has subclasses for PDFs and images, and automatically manages the context lifetime. Check out [WWDC16 session 205][1] for more details (image rendering begins around the 11:50 mark)

To be sure that it works on every device, use #available with a fallback to earlier versions of iOS:

extension UIView {

    // Using a function since `var image` might conflict with an existing variable
    // (like on `UIImageView`)
    func asImage() -> UIImage {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            return renderer.image { rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
        } else {
            UIGraphicsBeginImageContext(self.frame.size)
            self.layer.render(in:UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return UIImage(cgImage: image!.cgImage!)
        }
    }
}

[1]: https://developer.apple.com/videos/play/wwdc2016/205/ "What's New in Cocoa Touch"

Solution 2 - Ios

you can use extension

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in:UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: image!.cgImage!)
    }
}

Here the swift 3/4 version :

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in:UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: image!.cgImage!)
    }
}

Solution 3 - Ios

Convert your UIView to image by drawViewHierarchyInRect:afterScreenUpdates: which is many times faster than renderInContext

Important note: do not call this function from viewDidLoad or viewWillAppear , make sure you are capturing a view after it is it displayed /loaded fully

Obj C

     UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f);
     [myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO];
     UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

     myImageView.image =  snapshotImageFromMyView;

Save the edited image Photo album

     UIImageWriteToSavedPhotosAlbum(snapshotImageFromMyView, nil,nil, nil);

Swift 3/4

    UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.isOpaque, 0.0)
    myView.drawHierarchy(in: myView.bounds, afterScreenUpdates: false)
    let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    print(snapshotImageFromMyView)
    myImageView.image = snapshotImageFromMyView
      

Super easy generalization with extension , iOS11 , swift3/4

extension UIImage{
    convenience init(view: UIView) {

    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    self.init(cgImage: (image?.cgImage)!)
    
  }
}


Use : 
 //myView is completly loaded/visible , calling this code after only after viewDidAppear is call
 imgVV.image = UIImage.init(view: myView)
 // Simple image object
 let img =  UIImage.init(view: myView)

Solution 4 - Ios

On iOS 10:

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)
    }
}

Solution 5 - Ios

Best practice as of iOS 10 and Swift 3

while still supporting iOS 9 and earlier still works as of iOS 13, Xcode 11.1, Swift 5.1

extension UIView {
    
    func asImage() -> UIImage? {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            return renderer.image { rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
            defer { UIGraphicsEndImageContext() }
            guard let currentContext = UIGraphicsGetCurrentContext() else {
                return nil
            }
            self.layer.render(in: currentContext)
            return UIGraphicsGetImageFromCurrentImageContext()
        }
    }
}

I am unsure what the question means by:

> what is the best way to save it in an app (Not camera roll)?

Solution 6 - Ios

    UIGraphicsBeginImageContext(self.view.bounds.size);        
    self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
    var screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

Solution 7 - Ios

For example if I have a view of size: 50 50 at 100,100. I can use the following to take a screenshot:

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0);
    self.view.drawViewHierarchyInRect(CGRectMake(-50,-5-,view.bounds.size.width,view.bounds.size.height), afterScreenUpdates: true)
    var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();

Solution 8 - Ios

In my opinion, the approach with the initialiser isn't that great because it creates two images.

I prefer this:

extension UIView {
	var snapshot: UIImage? {
		UIGraphicsBeginImageContext(self.frame.size)
		guard let context = UIGraphicsGetCurrentContext() else {
			return nil
		}
		layer.render(in: context)
		let image = UIGraphicsGetImageFromCurrentImageContext()
		UIGraphicsEndImageContext()
		return image
	}
}

Solution 9 - Ios

This works for me for Xcode 9/Swift 3.2/Swift 4 and Xcode 8/Swift 3

 if #available(iOS 10.0, *) {

     // for Xcode 9/Swift 3.2/Swift 4 -Paul Hudson's code
     let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
     let capturedImage = renderer.image { 
         (ctx) in
         view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
     }
     return capturedImage

 } else {

     // for Xcode 8/Swift 3
     UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
     view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
     let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()
     return capturedImage!
 }

Here's how to use it inside a function:

fileprivate func captureUIImageFromUIView(_ view:UIView?) -> UIImage {

     guard (view != nil) else{
        
        // if the view is nil (it's happened to me) return an alternative image
        let errorImage = UIImage(named: "Error Image")
        return errorImage
     }

     // if the view is all good then convert the image inside the view to a uiimage
     if #available(iOS 10.0, *) {

         let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
         let capturedImage = renderer.image { 
             (ctx) in
             view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
         }
         return capturedImage

     } else {
            
         UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
         view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
         let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
         UIGraphicsEndImageContext()
         return capturedImage!
     }
}

Here's how to do something with the image returned from the function:

@IBOutlet weak fileprivate var myCustomView: UIView!
var myPic: UIImage?

let myImageView = UIImageView()

@IBAction fileprivate func saveImageButtonTapped(_ sender: UIButton) {

   myPic = captureUIImageFromUIView(myCustomView)

   // display the pic inside a UIImageView
   myImageView.image = myPic!
}

I got the Xcode 9/Swift 3.2/Swift 4 answer from Paul Hudson convert uiview to uiimage

I got the Xcode 8/Swift 3 from somewhere on SO a longgg time ago and I forgot where :(

Solution 10 - Ios

Swift 4.2, iOS 10

extension UIView {

    // If Swift version is lower than 4.2, 
    // You should change the name. (ex. var renderedImage: UIImage?)

    var image: UIImage? {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) }
    }
}

Sample

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .blue

let view2 = UIView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
view2.backgroundColor = .red
view.addSubview(view2)

let imageView = UIImageView(image: view.image)

enter image description here

Solution 11 - Ios

var snapshot = overView.snapshotViewAfterScreenUpdates(false)

or in objective-c

UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];

Solution 12 - Ios

You can use it easily by using the extension like this

// Take a snapshot from a view (just one view)
let viewSnapshot = myView.snapshot

// Take a screenshot (with every views in screen)
let screenSnapshot = UIApplication.shared.snapshot

// Take a snapshot from UIImage initialization
UIImage(view: self.view)

If you wanna use those extension method/variables, implement this

  1. UIImage extension

     extension UIImage {
         convenience init(view: UIView) {
             if let cgImage = view.snapshot?.cgImage {
                 self.init(cgImage: cgImage)
             } else {
                 self.init()
             }
         }
     }
    
  2. UIView extension

     extension UIView {
    
         var snapshot: UIImage? {
             UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
             if UIGraphicsGetCurrentContext() != nil {
                 drawHierarchy(in: bounds, afterScreenUpdates: true)
                 let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                 UIGraphicsEndImageContext()
                 return screenshot
             }
             return nil
         }
     }
    
  3. UIApplication extension

     extension UIApplication {
    
         var snapshot: UIImage? {
             return keyWindow?.rootViewController?.view.snapshot
         }
     }
    

Solution 13 - Ios

or iOS 10+ you can use the new UIGraphicsImageRenderer + the recommended drawHierarchy, which in some situations can be much faster than layer.renderInContext

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
        return renderer.image { _ in
            self.drawHierarchy(in: CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height), afterScreenUpdates: false)
        }
    }
}

Solution 14 - Ios

Thanks @Bao Tuan Diep ! I want add a supplement.

When you use the code:

yourView.layer.render(in:UIGraphicsGetCurrentContext()!)

You must notice that:

 - If you had used `autoLayout` or `Masonry` in `yourView` (that you want to convert) .
 - If you did not add `yourView` to another view which means that `yourView` was not used as a subview but just an object.

Then, your must use :

[yourView setNeedsLayout];
[yourView layoutIfNeeded];

to update yourView before yourView.layer.render(in:UIGraphicsGetCurrentContext()!).

Otherwise you may get an image object that contains no elements

Solution 15 - Ios

Swift 4.2

import Foundation
import UIKit  

extension UIImage {

    convenience init(view: UIView) {
    
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)
    
    } 
}

> using: > > > > let img = UIImage.init(view: self.holderView)

Solution 16 - Ios

Implementation in Swift 3 :

Add the code below , out of class scope .

extension UIImage {
    convenience init(_ view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)
    }
}

Usage :

let image = UIImage( Your_View_Outlet )

Solution 17 - Ios

I implemented @Naveed J.'s method like this, and it worked like a charm.

Here was his extentsion:

> extension UIView { >
> // Using a function since var image might conflict with an existing variable > // (like on UIImageView) > func asImage() -> UIImage { > let renderer = UIGraphicsImageRenderer(bounds: bounds) > return renderer.image { rendererContext in > layer.render(in: rendererContext.cgContext) > } > } > }

Here is how I implemented it.

//create an image from yourView to display
//determine the frame of the view/imageimage
let screen = self.superview!.bounds
let width = screen.width / 4 //make image 1/4 width of screen
let height = width
let frame = CGRect(x: 0, y: 0, width: width, height: height)
let x = (screen.size.width - frame.size.width) * 0.5
let y = (screen.size.height - frame.size.height) * 0.5
let mainFrame = CGRect(x: x, y: y, width: frame.size.width, height: frame.size.height)
            
let yourView = YourView() //instantiate yourView
yourView.frame = mainFrame //give it the frame
yourView.setNeedsDisplay() //tell it to display (I am not 100% sure this is needed)
            
let characterViewImage = yourView.asImage()

Solution 18 - Ios

Initializer with the new UIGraphicsImageRenderer available since iOS 10:

extension UIImage{
    convenience init(view: UIView) {

    let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
    let canvas = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height)
    let image = renderer.image { _ in
        self.drawHierarchy(in: canvas, afterScreenUpdates: false)
    }
    self.init(cgImage: (image?.cgImage)!)
  }
}

Solution 19 - Ios

work fine with me !

Swift4

 extension UIView {
        
    func toImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
        self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
        let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return snapshotImageFromMyView!
    }
    
    }

Solution 20 - Ios

For view contains blurred subview (e.g. UIVisualEffectView instance), only drawViewHierarchyInRect:afterScreenUpdates works.

@ViJay Avhad's answer is correct for this case.

Solution 21 - Ios

Using UIGraphicsImageRenderer doesn't work when the view contains Scene Kit subviews, Metal or Sprite Kit ones. In these cases, use

let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { ctx in
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}

Solution 22 - Ios

please try below code.
-(UIImage *)getMainImageFromContext
{
UIGraphicsBeginImageContextWithOptions(viewBG.bounds.size, viewBG.opaque, 0.0);
[viewBG.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSameer HussainView Question on Stackoverflow
Solution 1 - IosNaveed J.View Answer on Stackoverflow
Solution 2 - IosBao Tuan DiepView Answer on Stackoverflow
Solution 3 - IosViJay AvhadView Answer on Stackoverflow
Solution 4 - IosafrodevView Answer on Stackoverflow
Solution 5 - IosJon WillisView Answer on Stackoverflow
Solution 6 - IosVakasView Answer on Stackoverflow
Solution 7 - IosSameer HussainView Answer on Stackoverflow
Solution 8 - IosTinoView Answer on Stackoverflow
Solution 9 - IosLance SamariaView Answer on Stackoverflow
Solution 10 - IosDenView Answer on Stackoverflow
Solution 11 - IosYedidya ReissView Answer on Stackoverflow
Solution 12 - IosKhemmachart ChutapetchView Answer on Stackoverflow
Solution 13 - IosМортView Answer on Stackoverflow
Solution 14 - IosguozqzzuView Answer on Stackoverflow
Solution 15 - Iosreza_khalafiView Answer on Stackoverflow
Solution 16 - IosroyView Answer on Stackoverflow
Solution 17 - IosJacob F. Davis C-CISOView Answer on Stackoverflow
Solution 18 - IosFrédéric AddaView Answer on Stackoverflow
Solution 19 - IosFarisView Answer on Stackoverflow
Solution 20 - IosrafalkittaView Answer on Stackoverflow
Solution 21 - IosjohnnyMacView Answer on Stackoverflow
Solution 22 - Iosherry.masterView Answer on Stackoverflow