How to convert a UIView to an image
Asked Answered
W

24

139

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)
Whoops answered 7/6, 2015 at 17:15 Comment(1)
M
290

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!)
        }
    }
}
Musil answered 22/12, 2016 at 16:56 Comment(14)
This should be marked as the correct answer. All other options will cause the rendered image to lose clarity and look pixelated/blurry.Unlettered
The only right way! For beginners the usage is: let image = customView.asImage()Pit
Is it possible to render the UIView with a transparent background? Mine has some rounded corners.Maniac
@Maniac 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();Musil
Does not work for view contains blurred subview (e.g. UIVisualEffectView instance).Beguin
Thank you! If anyone knows, I'd be curious to know what differences there are with the code from hackingwithswift.com/example-code/media/…: return renderer.image { ctx in drawHierarchy(in: bounds, afterScreenUpdates: true) }Brietta
@NaveedJ. if you see this. Someone has copy/pasted your answer, into an unrelated question, in an attempt to get a bounty! https://mcmap.net/q/168069/-render-a-cgimage-rather-than-uiimage-directlyFoushee
If the view contains SCNScene subviews, this doesn't workBankbook
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) }Ventricle
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 !!!!Spanner
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 zeroWagers
Absolutely brilliant. Such an easy and precise solution- thank youOutofdoors
somehow, in my situation, this doesn't seem to consider the sublayers I use to draw shadowsReticulation
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?Solid
K
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!)
    }
}
Kaftan answered 11/11, 2015 at 4:37 Comment(10)
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?Mayonnaise
let image = UIImage(view: myView)Zootoxin
Doesn't work for me as UIGraphicsGetCurrentContext() can be nil and the code crashes.Attest
@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.Toque
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 pleaseAgrestic
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)Forcible
I'm using same code but getting memory issue and application crashing.... ?Lilias
The total lack of error checking diminishes the value of this example considerably.Byelorussian
In Swift 4, it's better to use UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0). Or the image you get will become blurry.Sorel
Does not work for view contains blurred subview (e.g. UIVisualEffectView instance).Beguin
M
51

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)
Manila answered 11/8, 2016 at 10:53 Comment(10)
No working for me I got a blackScreen for my UiImageRogerson
Really ?? Can you paste your code here.. U might be using wrong UIView object to be convert as UIImage.Manila
` func convertViewIntoImg() -> Void { imgFakeCard.image = UIImage.init(view: card) } ` With your extension in swift 3 this function is called in viewDidLoadRogerson
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.Manila
That work greats thanks, the problem came from where I called your extensionRogerson
In case one of view's layer is CAEAGLLayer, renderInContext doesn't work. drawHierarchy is not only faster but also working for CAEAGLLayer.Interferon
has anyone else notice a serious increase of memory while using this code?Chaunce
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.Concentrate
Please help me to convert view in proper image.Concentrate
I explicitly passed true instead of view.isOpaque and passed true for afterScreenUpdates and it works great. Thanks!Schaub
C
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)!)
    }
}
Church answered 30/9, 2016 at 14:24 Comment(2)
this leads to crashes.Graptolite
@Graptolite this will lead to a crash if the passed view's width and height are 0.Outgo
D
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)?

Dochandorrach answered 10/8, 2017 at 20:42 Comment(0)
M
17
    UIGraphicsBeginImageContext(self.view.bounds.size);        
    self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
    var screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
Merwyn answered 7/6, 2015 at 17:48 Comment(5)
developer.apple.com/library/ios/documentation/UIKit/Reference/…Reward
UIGraphicsGetImageFromCurrentImageContext returns UIImage. No need to cast it to UIImageReward
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!Whoops
@Merwyn when i tried to print image.it display too small.what is solution for thisDevorahdevore
The screenshot image does not have good quality, what should I do?Pasia
W
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();
Whoops answered 7/6, 2015 at 19:26 Comment(2)
in self.view line there is mistake in parameter.i think it should be -5 not -5-Devorahdevore
@sameer i have one question.when i print this image it is too smallDevorahdevore
P
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
    }
}
Padus answered 8/12, 2016 at 10:0 Comment(2)
You should call the property something other than image. This can interfere with subclasses that have properties called image, such as UIImageView.Condense
Followed @HobbestheTige 's suggestion and renamed the propertyPadus
S
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 :(

Spermophyte answered 29/10, 2017 at 17:22 Comment(21)
Also where does the image get displayed and/or saved?Hara
@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].myPicSpermophyte
@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?Spermophyte
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?Hara
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.Hara
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 minutesSpermophyte
@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 thoughSpermophyte
@FamicTech I'm going to add what to do with the image in 2 minutes. Look at the adjusted codeSpermophyte
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.Hara
@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?Spermophyte
also if you know how to do it by saving the collection view in a pdf I would be OK with that too.Hara
Once you get the image here is how you convert it to a pdf: https://mcmap.net/q/168070/-converting-uiimageview-to-pdf-swiftSpermophyte
@FamicTech are you using storyboard and do you understand programmatic code? What I email is going to be programmaticSpermophyte
@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: #34078306Spermophyte
@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 youSpermophyte
I have been looking into this project... I have two errors but its pretty cool concept if it really works... github.com/romainmenke/ScrollViewImagerHara
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 :)Spermophyte
@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?Spermophyte
Let us continue this discussion in chat.Spermophyte
here, I just posted the answer to our conversation here. If it works let me know. https://mcmap.net/q/168072/-swift-cropping-a-screenshot-without-tabbar-and-navigationbarSpermophyte
@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 thisSpermophyte
N
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

Nepean answered 16/8, 2018 at 2:35 Comment(2)
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'.Acquit
@Acquit 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.Nepean
P
5
var snapshot = overView.snapshotViewAfterScreenUpdates(false)

or in objective-c

UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];
Philippians answered 7/6, 2015 at 18:11 Comment(1)
Is imageView same as image ? If so how do i save it in my app ?Whoops
M
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
        }
    }
    
Malignant answered 24/8, 2017 at 16:18 Comment(1)
i had view with subviews zPositions manipulated, and other answers didn't give me correct layer positions, thank you.Landmeier
N
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)
        }
    }
}
Nectar answered 18/8, 2017 at 18:15 Comment(0)
U
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

Uneven answered 29/11, 2017 at 2:51 Comment(0)
S
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)

Suspender answered 22/1, 2019 at 7:32 Comment(0)
B
2

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)
}
Bankbook answered 1/9, 2020 at 1:14 Comment(0)
T
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

Treenware answered 21/11, 2022 at 12:12 Comment(0)
E
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 )
Eggplant answered 17/7, 2017 at 7:53 Comment(1)
getting error "Drawing a view (0x1040a6970, UIView) that has not been rendered at least once requires"Helicline
L
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

Liveryman answered 3/10, 2022 at 16:28 Comment(0)
K
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()
Kiyokokiyoshi answered 27/6, 2017 at 23:26 Comment(0)
S
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)!)
  }
}
Scummy answered 26/7, 2018 at 8:22 Comment(0)
S
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!
    }

    }
Surrebutter answered 3/8, 2018 at 18:53 Comment(1)
return snapshotImageFromMyView! Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional valueHenbit
B
0

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

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

Beguin answered 11/9, 2018 at 0:40 Comment(0)
O
-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;
}
Oliy answered 23/10, 2017 at 11:29 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.