19

First of i Add the UILable on UIImageView and then after i screenshot the UIView, the image not proper capture the UIView Frame i also attach the image url.

enter image description here

1) Original Image Link:

enter image description here

2) After Capture the image Link:

Code:

UIGraphicsBeginImageContext(viewImage.frame.size);
[viewImage.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *vwImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

NSData *data=UIImagePNGRepresentation(vwImage);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// NSString *imgName = [NSString stringWithFormat:imagename];
NSString *strPath = [documentsDirectory stringByAppendingPathComponent:imagename];
[data writeToFile:strPath atomically:YES];
Cœur
  • 37,241
  • 25
  • 195
  • 267
Rushabh
  • 3,208
  • 5
  • 28
  • 51
  • I think the screenshot was taken correctly. The problem I see on the other side. Are you sure, that your UIView has the same size as the source image? If no, then do you add the UIImage to your view without changing its size? Are you sure? :) 'Couse if you have an image of size 200x500, but you drew it on UIView of size 300x700 allowing UIView to stretch the image for it to occupy all the space, then you'll have a screenshot of the UIView size, not the source image – makaron Jun 17 '13 at 10:20
  • when i save that capture image in photo library at that time the date in not proper display – Rushabh Jun 17 '13 at 10:23
  • Ah, ok, got it now. Try to check out my answer here: http://stackoverflow.com/questions/16062974/place-an-image-on-top-of-another-image/16064804#16064804 There's a fully working code for your purpose – makaron Jun 17 '13 at 10:26
  • Answer using API with better performance http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina-display#25585958 – Khaled Annajar Nov 13 '16 at 09:13

7 Answers7

28

You can take the screenshots of any UIView or capture any UIView and save it as an image with below code.

- (UIImage *)captureView {

    //hide controls if needed
    CGRect rect = [self.view bounds];

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.view.layer renderInContext:context];   
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;

}
Paras Joshi
  • 20,427
  • 11
  • 57
  • 70
15

Take snapshot of a UIView

pass your view to this function it will handle everything itself( No need to set bounds or frames)

- (UIImage *)takeSnapshotOfView:(UIView *)view
{
    UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width, view.frame.size.height));
    [view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height) afterScreenUpdates:YES];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
  • [view.layer renderInContext:ctx] solution proposed everywhere on internet was throwing CGGetColorAlpha EXC_BAD_ACCESS, but this worked like a charm. Thank you! – Igor Perić Aug 23 '17 at 11:50
  • 1
    Big upvote for drawViewHierarchyInRect rather than .layer renderInContext. It has better quality and in my usecase was the only one that worked, as it includes the whole hierarchy – p0358 Nov 18 '20 at 18:35
10

Swift 3 Version:

func takeSnapshotOfView(view:UIView) -> UIImage? {
    UIGraphicsBeginImageContext(CGSize(width: view.frame.size.width, height: view.frame.size.height))
    view.drawHierarchy(in: CGRect(x: 0.0, y: 0.0, width: view.frame.size.width, height: view.frame.size.height), afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Marckaraujo
  • 7,422
  • 11
  • 59
  • 97
4

In Swift 5 as extension:

extension UIView {

    func takeSnapshot() -> UIImage? {
        UIGraphicsBeginImageContext(CGSize(width: self.frame.size.width, height: self.frame.size.height - 5))
        let rect = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height)
        drawHierarchy(in: rect, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

extension UIImage {

    func saveToPhotoLibrary(_ completionTarget: Any?, _ completionSelector: Selector?) {
        DispatchQueue.global(qos: .userInitiated).async {
            UIImageWriteToSavedPhotosAlbum(self, completionTarget, completionSelector, nil)
        }
    }
}

extension UIAlertController {

    func present() {
        guard let controller = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.rootViewController else {
            return
        }
        controller.present(self, animated: true)
    }
}

In your UIView sub class:

func saveImage() {
    let selector = #selector(self.onImageSaved(_:error:contextInfo:))
    takeSnapshot()?.saveToPhotoLibrary(self, selector)
}

@objc private func onImageSaved(_ image: UIImage, error: Error?, contextInfo: UnsafeRawPointer) {
    if let error = error {
        let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        ac.present()
    } else {
        let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        ac.present()
    }
    onImageSaved?()
}
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
2

you can use the below code to

UIGraphicsBeginImageContext(pictureView.bounds.size); 
                    
[pictureView.layer renderInContext:UIGraphicsGetCurrentContext()]; 
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Sonu
  • 248
  • 1
  • 10
0

Try like this,

UIGraphicsBeginImageContext(viewImage.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, viewImage.frame);     

[viewImage.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *vwImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

NSData *data=UIImagePNGRepresentation(vwImage);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// NSString *imgName = [NSString stringWithFormat:imagename];
NSString *strPath = [documentsDirectory stringByAppendingPathComponent:imagename];
[data writeToFile:strPath atomically:YES];
Venk
  • 5,949
  • 9
  • 41
  • 52
0

Try this if you want captured image maintain its original quality.

- (UIImage *)captureViewWithAllSubView:(UIView *)view {
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f);
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
    UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return capturedImage;
}
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Mehsam Saeed
  • 235
  • 2
  • 14