134

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)
gaynorvader
  • 2,619
  • 3
  • 18
  • 32
Sameer Hussain
  • 2,421
  • 8
  • 23
  • 41

24 Answers24

280

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 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!)
        }
    }
}
John Montgomery
  • 6,739
  • 9
  • 52
  • 68
Naveed J.
  • 3,176
  • 2
  • 12
  • 11
  • 15
    This should be marked as the correct answer. All other options will cause the rendered image to lose clarity and look pixelated/blurry. – TuplingD Dec 07 '17 at 20:41
  • 3
    The only right way! For beginners the usage is: let image = customView.asImage() – Fabio Jun 20 '18 at 07:35
  • 2
    Is it possible to render the UIView with a transparent background? Mine has some rounded corners. – ixany Aug 09 '18 at 22:31
  • @ixany This should already take care of it. I just tried it with the following in a Playground and it worked: `let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)); view.backgroundColor = .black; view.layer.cornerRadius = 9; view.asImage();` – Naveed J. Aug 10 '18 at 19:20
  • Does not work for view contains blurred subview (e.g. UIVisualEffectView instance). – rafalkitta Sep 11 '18 at 00:41
  • 2
    Thank you! If anyone knows, I'd be curious to know what differences there are with the code from https://www.hackingwithswift.com/example-code/media/how-to-render-a-uiview-to-a-uiimage: return renderer.image { ctx in drawHierarchy(in: bounds, afterScreenUpdates: true) } – Kqtr Feb 21 '19 at 09:23
  • @NaveedJ. if you see this. Someone has copy/pasted your answer, into an unrelated question, in an attempt to get a bounty! https://stackoverflow.com/a/59624206/294884 – Fattie Jan 07 '20 at 22:38
  • 1
    If the view contains SCNScene subviews, this doesn't work – johnnyMac Sep 01 '20 at 01:07
  • 1
    if the view contains GLKView, this also doesn't work, the code bellow works equally well no matter what the view contains - if you're using UIKit, SpriteKit, Metal or whatever, it all works.```let renderer = UIGraphicsImageRenderer(size: view.bounds.size) let image = renderer.image { ctx in view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) }``` – king wang Nov 13 '20 at 03:20
  • This worked like a charm for me on iOS 14.0 swift 5.2 !! Actually I was using self.drawHierarchy(in: self.bounds, afterScreenUpdates: true) for a stack view with multiple subviews inside a scrollview but for stack view with multiple stack views as subviews it was just not working. This saved my weekend, magic code !!!! – gagan sharma Apr 18 '21 at 12:01
  • I have a swiftUI View first I converted that View to UIView using UIHostingController and when i use self.view.addSubview(myView.view) it working fine and showing but when I am using this extension to assign that image to storyboard imageview its not working I have printed the height and width and its returning zero – Arslan Kaleem Apr 01 '22 at 17:04
  • Absolutely brilliant. Such an easy and precise solution- thank you – Henry Heleine Jul 19 '22 at 20:46
  • somehow, in my situation, this doesn't seem to consider the sublayers I use to draw shadows – João Serra Sep 30 '22 at 14:34
  • While using this approach to convert UIview to UIImage, I saw a big spike in Memory usage(from 120Mb it jumps to 900+Mb). Where as using the second answer, there wasn't any such spike. Can anyone please help me understand this phenemenon? – Hridoy Chowdhury Jan 07 '23 at 07:49
69

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!)
    }
}
shreyasm-dev
  • 2,711
  • 5
  • 16
  • 34
Bao Tuan Diep
  • 2,221
  • 2
  • 17
  • 23
  • 7
    I'm embarrassed to be the one asking this, but how do I call this? I have no image and a UIView that I want to be an image. The above code gives me a UIImage extension... but now what? – Dave G Aug 11 '16 at 16:36
  • 12
    `let image = UIImage(view: myView)` – doctorBroctor Aug 11 '16 at 18:48
  • 2
    Doesn't work for me as UIGraphicsGetCurrentContext() can be nil and the code crashes. – Juan Carlos Ospina Gonzalez Dec 08 '16 at 15:55
  • @JuanCarlosOspinaGonzalez If UIGraphicsGetCurrentContext returned nil then I suspect you need to check UIGraphicsBeginImageContext to figure out why it failed. My guess is that one of the dimensions of your size is zero or otherwise invalid, but I have no idea really what could cause that to fail. – John Stephen Jan 05 '17 at 23:41
  • i'm searching for same functionality to work on background thread, like `https://joris.kluivers.nl/blog/2009/11/27/background-image-processing/`with CGContext, Can you suggest please – anjnkmr Mar 30 '17 at 06:23
  • 2
    you should also keep in mind the scale of the screen, so i would replace the first line of the initializer with this: `UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.main.scale)` – iVentis Dec 06 '17 at 12:38
  • I'm using same code but getting memory issue and application crashing.... ? – BalKrishan Yadav Mar 12 '18 at 07:23
  • The total lack of error checking diminishes the value of this example considerably. – SafeFastExpressive May 17 '18 at 20:46
  • In Swift 4, it's better to use `UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)`. Or the image you get will become blurry. – yancaico Aug 17 '18 at 08:26
  • Does not work for view contains blurred subview (e.g. UIVisualEffectView instance). – rafalkitta Sep 11 '18 at 00:42
50

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)
ViJay Avhad
  • 2,684
  • 22
  • 26
  • 4
    No working for me I got a blackScreen for my UiImage – Badr Filali Jun 29 '17 at 12:47
  • Really ?? Can you paste your code here.. U might be using wrong UIView object to be convert as UIImage. – ViJay Avhad Jun 29 '17 at 13:10
  • ` func convertViewIntoImg() -> Void { imgFakeCard.image = UIImage.init(view: card) } ` With your extension in swift 3 this function is called in viewDidLoad – Badr Filali Jun 29 '17 at 14:38
  • 1
    Thanks for posting your code and mentioning that you have called in viewDidLoad. Here is what you went wrong. You are capturing a snapshot of view before it is being loaded in memory. Please try calling code in viewDidAppear, that will solve the problem for sure. Any question please reply. – ViJay Avhad Jun 30 '17 at 06:43
  • That work greats thanks, the problem came from where I called your extension – Badr Filali Jun 30 '17 at 08:44
  • In case one of view's layer is `CAEAGLLayer`, `renderInContext` doesn't work. `drawHierarchy` is not only faster but also working for `CAEAGLLayer`. – Ryan Jul 13 '17 at 21:46
  • has anyone else notice a serious increase of memory while using this code? – rcat24 Aug 10 '17 at 13:36
  • Hay @ViJayAvhad, I am created view programmatically in viewDidLoad method. If I am use "renderInContext" it created blurred image. So I use "drawViewHierarchyInRect:afterScreenUpdates:" it display black image. When we create view in xib it convert proper. But my need is create view programmatically. – Chirag Kothiya Aug 22 '18 at 12:51
  • Please help me to convert view in proper image. – Chirag Kothiya Aug 22 '18 at 12:51
  • I explicitly passed `true` instead of `view.isOpaque` and passed `true` for `afterScreenUpdates` and it works great. Thanks! – jangelsb Nov 16 '21 at 06:16
20

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)!)
    }
}
afrodev
  • 1,244
  • 11
  • 11
20

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

Jon Willis
  • 6,993
  • 4
  • 43
  • 51
17
    UIGraphicsBeginImageContext(self.view.bounds.size);        
    self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
    var screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
Vakas
  • 6,291
  • 3
  • 35
  • 47
  • https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKitFunctionReference/#//apple_ref/c/func/UIGraphicsGetImageFromCurrentImageContext – Leo Dabus Jun 07 '15 at 18:07
  • 1
    UIGraphicsGetImageFromCurrentImageContext returns UIImage. No need to cast it to UIImage – Leo Dabus Jun 07 '15 at 18:08
  • This takes screenshot of the entire view yeah ? I just want to convert a UIView i.e overview in my code which is subview of self.view to an image. Not the entire view! – Sameer Hussain Jun 07 '15 at 18:28
  • @Vakas when i tried to print image.it display too small.what is solution for this – Krutarth Patel Sep 24 '16 at 07:44
  • 7
    The screenshot image does not have good quality, what should I do? – Neela Mar 31 '17 at 07:25
13

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();
Sameer Hussain
  • 2,421
  • 8
  • 23
  • 41
8

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
    }
}
Tino
  • 117
  • 2
  • 4
6

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 :(

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Also where does the image get displayed and/or saved? – Famic Tech Dec 24 '18 at 16:40
  • @FamicTech once the image is created it's up to youth decide what to do with it. In the example from my code I just converted the UIView to an UIImage named "myPic" but I didn't do anything with 'myPic'. The next possible step is to display 'myPic' inside an UIImageVIew like this: myImageVIew.image = myPic. If you are using a collectionView and you wanted to display images in it then you create an UIImageVIew in each cell and then display the image (myPic) inside the UIImageVIew via the indexPath.item from the datasource ex: myImageView.image = dataSource[indexPath.item].myPic – Lance Samaria Dec 24 '18 at 16:58
  • @FamicTech You are making a slight mistake. This has nothing to do with a collectionVIew. You have 2 different class types: UIImage and a UIView. Both of them can be used to display any image but they both do it in different ways. Pretty much everything in iOS is a subclass of a UIView but a UIImage only can display images (2 different things). What this function does is take the UIVIew and convert it to a UIImage. An easier eg. If you want to convert 4.0 (a Double) to 4 (an Int) you cast it Int(4.0) result is 4. But with this you convert the image inside the UIVIew to a UIImage. Understand? – Lance Samaria Dec 24 '18 at 17:04
  • what I am trying to do is have the user come to my Collection View Controller and press a button that should take the current view, which happens to be a Collection View 10x10 and create an image of the entire 10x10 grid (please note that the entire grid is not fully visible in view). Does your code above solve for that use case? – Famic Tech Dec 24 '18 at 17:22
  • The button is in the Navigation Controller. User would click it and it 'should' take the Collection View displayed under the navigation controller and create an Image of the Collection View (a PDF would work as well too) with all the information in the Cells in the Collection View. – Famic Tech Dec 24 '18 at 17:34
  • Ok now I understand. Technically it should work because a collectionIVew is a subclass of a UIView. I'll try it in one of my own projects. Gimme a few minutes – Lance Samaria Dec 24 '18 at 17:36
  • @FamicTech yes it 100% works, I just tried it and the returned image was an image of the collectionView with the cells that are currently displayed on the screen. All you have to put in is: let newPic = captureUIImageFromUIView(collectionView). It's up to you what you want to do with newPic. it's just an image, I guess display it somewhere or save it to somewhere like firebase. That's a different conversation though – Lance Samaria Dec 24 '18 at 17:47
  • @FamicTech I'm going to add what to do with the image in 2 minutes. Look at the adjusted code – Lance Samaria Dec 24 '18 at 17:50
  • so it will only display the portion of the Collection View on the screen? Is there anyway to capture even the part of the collection view that is off the screen, since I have scroll bars vertically and horizontally since the collection view is 10x10 and can not fit entirely on the screen at once. – Famic Tech Dec 24 '18 at 17:57
  • @FamicTech I think I understand your question. It sounds like you want to take a picture of everything on the screen except the navigationBar, is that correct? – Lance Samaria Dec 24 '18 at 18:04
  • also if you know how to do it by saving the collection view in a pdf I would be OK with that too. – Famic Tech Dec 24 '18 at 18:14
  • Once you get the image here is how you convert it to a pdf: https://stackoverflow.com/a/45001576/4833705 – Lance Samaria Dec 24 '18 at 18:46
  • @FamicTech are you using storyboard and do you understand programmatic code? What I email is going to be programmatic – Lance Samaria Dec 24 '18 at 18:47
  • @FamicTech I think there's a much easier way for you to do this, I'm looking now. It might be overkill for you to so what i suggested, you might simply need to take a screenshot to the view itself minus the navigationBar: https://stackoverflow.com/questions/34077806/how-do-i-make-a-snapshot-without-the-navigation-bar-in-ios-with-swift – Lance Samaria Dec 24 '18 at 20:22
  • @FamicTech I'm still working on the code gimme a few. I'll email it to you when I'm done. There's a blank space where the navigationBar is that I'm trying to crop out for you – Lance Samaria Dec 24 '18 at 20:26
  • I have been looking into this project... I have two errors but its pretty cool concept if it really works... https://github.com/romainmenke/ScrollViewImager – Famic Tech Dec 24 '18 at 21:02
  • I'll look in a few, I'm having problems cropping the navBar. I've actually been working on this for almost 1.5 hrs, I didn't think it would take this long but then again I didn't factor in the navBar. I don't mind though because this might be a problem I run into one day so I rather solve it now then later. I'm actually happy you brought this problem forward :) – Lance Samaria Dec 24 '18 at 21:06
  • @FamicTech I finally found the answer. It all these hours but it's done. Do you want me to email you the project or did you figure it out? – Lance Samaria Dec 24 '18 at 22:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/185744/discussion-between-lance-samaria-and-famic-tech). – Lance Samaria Dec 24 '18 at 23:21
  • here, I just posted the answer to our conversation here. If it works let me know. https://stackoverflow.com/a/53918349/4833705 – Lance Samaria Dec 24 '18 at 23:39
  • @FamicTech the version I emailed you didn't include a tabBar. I'm not sure if you have one or not. Check the link I posted, it accounts for the tabor if you do have one. Please let me know if the worked. I sent several hrs on this – Lance Samaria Dec 25 '18 at 00:37
6

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

Den
  • 3,179
  • 29
  • 26
  • Just wanted to point out that this needs to be named something else if you are using any UIImageViews in your project, otherwise .image becomes read only. Maybe call it 'screenCapture' instead of 'image'. – Chris Sep 14 '18 at 18:55
  • @Chris Yeah, you are right. You should change a name if Swift version is lower than 4.2. Thank you for your pointing out the mistake. – Den Sep 15 '18 at 11:25
5
var snapshot = overView.snapshotViewAfterScreenUpdates(false)

or in objective-c

UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];
Yedidya Reiss
  • 5,316
  • 2
  • 17
  • 19
4

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

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)
        }
    }
}
Морт
  • 1,163
  • 9
  • 18
3

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

guozqzzu
  • 839
  • 10
  • 12
3

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)

reza_khalafi
  • 6,230
  • 7
  • 56
  • 82
2

I use this for small images (because it does not work for big images):

extension UIView {
    public func drawHierarchyAsImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
        let image = renderer.image { _ in
            self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
        }
        return image
    }
}

For high resolution images i use this:

extension UIView {
    public func renderAsImage(scale: CGFloat) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale

        let renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
        return renderer.image { rendererContext in
            if let presentation = layer.presentation() {
                presentation.render(in: rendererContext.cgContext)
            }
        }
    }
}

In my view hierarchy i have rotated views and thats why i use the presentation layer as mentioned here: renderInContext does not capture rotated subviews

Janevin
  • 63
  • 4
1

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 )
roy
  • 6,685
  • 3
  • 26
  • 39
1

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)
}
johnnyMac
  • 381
  • 2
  • 14
1

Resorting to an extension, as some have suggested, might be okay for the common case yet not ideal in every application, depending on how one is drawing the view, i.e. as layers, or trying to display a view to its subviews.

If one is creating an image of the subview hierarchy:

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

However, if your view consists of sublayers, for example CALayer, including CAShapeLayer comprised of UIBezierCurve... Something more like this may be in order:

    let renderer = UIGraphicsImageRenderer(bounds: view.bounds)
    let image = renderer.image { rendererContext in
        for sublayer in self.layer.sublayers!.reversed() {
            sublayer.render(in: rendererContext.cgContext)
        }
    }

And of course one might want to display a view with subviews which are layered. And since UIView.drawHierarchy() doesn't include sublayers... well, it could get more complicated

clearlight
  • 12,255
  • 11
  • 57
  • 75
0

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()
0

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)!)
  }
}
Frederic Adda
  • 5,905
  • 4
  • 56
  • 71
0

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

    }
Faris
  • 1,206
  • 1
  • 12
  • 18
  • ```return snapshotImageFromMyView!``` Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value – Takasur Dec 08 '18 at 06:33
0

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

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

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