159

I want a screenshot of the image on the screen saved into the saved photo library.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jab
  • 26,853
  • 21
  • 75
  • 114

20 Answers20

233

Considering a check for retina display use the following code snippet:

#import <QuartzCore/QuartzCore.h> 

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
} else {
    UIGraphicsBeginImageContext(self.window.bounds.size);
}

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}
Ricardo Sanchez-Saez
  • 9,466
  • 8
  • 53
  • 92
DenNukem
  • 8,014
  • 3
  • 40
  • 45
  • 14
    Note that this won't capture certain types of screen content, like OpenGL ES layers. UIGetScreenImage(), that Hua-Ying points to, does. – Brad Larson Feb 05 '10 at 06:01
  • 12
    Dear people, don't forget to do #import – Enrico Susatyo Oct 12 '12 at 07:15
  • will this take a screenshot if you are viewing a picture your app just took? – John Riselvato Dec 06 '12 at 04:36
  • Hi DenNukem, this work perfectly in my other apps, but in current project, First time it capture perfectly but second time it crash the app with this "malloc: *** error for object 0x1e868334: incorrect checksum for freed object - object was probably modified after being freed. *** set a breakpoint in malloc_error_break to debug". Any solution for that ? or i doing some wrong i cant get it. Thanks. – iBhavik May 28 '13 at 11:16
  • 5
    Where is this image stored? – Burhanuddin Sunelwala Aug 20 '13 at 06:44
  • @BurhanuddinSunelwala i found one solution for this at last add this code UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:data], nil, nil, nil); you found your image in photo album path – Jignesh B Sep 20 '13 at 09:34
  • Use `UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, 0.0);` this will create image context of the current devices scale. – Julian F. Weinert Oct 30 '13 at 16:17
  • Above code works well but when try downscale the image little bit the uilabels are blurred even if i maintain the aspect ratio for resizing? what is the reason? – jailani May 19 '14 at 12:35
  • 4
    @wladimir-palant : What about Key-Board , Because when i use to take screen shot of sceen where user type , then key board not capture ,how can i add key-Board in screen capture..? – g212gs Jul 04 '14 at 04:56
  • No need for "[UIScreen mainScreen].scale". Simply passing "0" will automatically set the scale to the scale factor of the device’s main screen. – Almog C May 04 '15 at 11:58
  • drawViewHierarchyInRect is faster than renderInContext according to http://stackoverflow.com/questions/19066717/how-to-render-view-into-image-faster so it is better to use Mann 's below code – alones May 01 '16 at 15:38
  • Does Apple approve this functionality ? Because without user notification we are doing this right – Sridhar Jun 22 '16 at 07:30
49

Below method works for OPENGL objects also

//iOS7 or above
- (UIImage *) screenshot {

    CGSize size = CGSizeMake(your_width, your_height);

    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

    CGRect rec = CGRectMake(0, 0, your_width, your_height);
    [_viewController.view drawViewHierarchyInRect:rec afterScreenUpdates:YES];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Mann
  • 5,477
  • 6
  • 45
  • 57
  • 5
    `UIGetScreenImage` is a private API – jonahb Jan 02 '14 at 01:01
  • Thanks a lot for this! You definitely get my vote. After attempting multiple other ways, it appears that drawViewHierarchy is the only way how a screen grab can be taken in robovm with a javafx ui. – Christo Smal Oct 07 '14 at 07:44
  • This is what is working everytime!!!! I have a lot of 3d transforms on the scrreen and other solutions did mess in my screenshot. This works like it should. Thanks a lot! – AndrewK Jul 23 '15 at 14:04
  • 1
    if the view controller is in a navigation controller the navigation bar is not captured – Radu Simionescu Sep 04 '15 at 11:32
  • Yes, drawViewHierarchyInRect is faster than renderInContext :-) – alones May 01 '16 at 15:40
20
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

NSData *imageData = UIImageJPEGRepresentation(image, 1.0 ); //you can use PNG too
[imageData writeToFile:@"image1.jpeg" atomically:YES];
Julian
  • 9,299
  • 5
  • 48
  • 65
IOS Rocks
  • 2,127
  • 2
  • 21
  • 24
14
- (UIImage*) getGLScreenshot {
    NSInteger myDataLength = 320 * 480 * 4;

    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y <480; y++)
    {
        for(int x = 0; x <320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
        }
    }

    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);

    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}

- (void)saveGLScreenshotToPhotosAlbum {
    UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);  
}

Source.

Aistina
  • 12,435
  • 13
  • 69
  • 89
  • 1
    Why is myDataLength multiplied by 4 ontop of 320 * 480? – Jab Feb 04 '10 at 15:23
  • 2
    This also really does seem very stupid that they don't provide us with an easier way of doing this. – Jab Feb 04 '10 at 15:26
  • 320 * 480 is the number of pixels. Times four so you have RGBA for each pixel. – Aistina Feb 04 '10 at 15:27
  • remember to multiple by the scale factor when running on an iPhone 4 :-) – MrDatabase Feb 05 '11 at 19:51
  • Hi, first of all thanks for sharing this, it saved my week :) But I'm having a problem here, when I take an screenshot from an iphone4 with the retina display enabled, I'm getting a really big image from the openglView, i have tried to rescale the resulting UIImage with no results, anyone can help me with this? thanks in advance – JonLOo Sep 29 '11 at 11:59
  • Ok done just multiply the scale factor with the width and height :) – JonLOo Sep 29 '11 at 12:14
  • this code works only on OpenGL Apps? on a regular app it gives black image... how can i use this? – orthehelper Jun 27 '12 at 11:14
  • Same code is not working in iOS 6 (in devide). Anyone know how to fix this issue? – Dee Oct 09 '12 at 14:18
  • 1
    @Aistina , use `UIImage *myImage = [[UIImage alloc] initWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationDownMirrored];` and you don't need 'buffer2' anymore. BTW it seems to me like you forgot about memory management, no free() or release() calls... – Oleg Trakhman Jun 06 '13 at 10:16
11

IN SWIFT

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}
Aviel Gross
  • 9,770
  • 3
  • 52
  • 62
Vicky
  • 1,095
  • 16
  • 15
10

As of iOS10, this gets a bit simpler. UIKit comes with UIGraphicsImageRender that allows you to

... accomplish drawing tasks, without having to handle configuration such as color depth and image scale, or manage Core Graphics contexts

Apple Docs - UIGraphicsImageRenderer

So you can now do something like this:

let renderer = UIGraphicsImageRenderer(size: someView.bounds.size)

let image = renderer.image(actions: { context in
    someView.drawHierarchy(in: someView.bounds, afterScreenUpdates: true)
})

Many of the answers here worked for me in most cases. But when trying to take a snapshot of an ARSCNView, I was only able to do it using the method described above. Although it might be worth noting that at this time, ARKit is still in beta and Xcode is in beta 4

Tulio Troncoso
  • 306
  • 5
  • 6
9

See this post it looks like you can use UIGetScreenImage() for now.

MendelG
  • 14,885
  • 4
  • 25
  • 52
Hua-Ying
  • 3,166
  • 4
  • 23
  • 25
7

This will save a screenshot and as well return the screenshot too.

-(UIImage *)capture{
    UIGraphicsBeginImageContext(self.view.bounds.size);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *imageView = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageWriteToSavedPhotosAlbum(imageView, nil, nil, nil); //if you need to save
    return imageView;
}
A. Adam
  • 3,094
  • 1
  • 26
  • 25
5

I think the following snippet would help if you want to take a full screen(except for status bar),just replace AppDelegate with your app delegate name if necessary.

- (UIImage *)captureFullScreen {

    AppDelegate *_appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        // for retina-display
        UIGraphicsBeginImageContextWithOptions(_appDelegate.window.bounds.size, NO, [UIScreen mainScreen].scale);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    } else {
        // non-retina-display
        UIGraphicsBeginImageContext(_bodyView.bounds.size);
        [_appDelegate.window drawViewHierarchyInRect:_appDelegate.window.bounds afterScreenUpdates:NO];
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
tounaobun
  • 14,570
  • 9
  • 53
  • 75
4

I couldn't find an answer with Swift 3 implementation. So here it goes.

static func screenshotOf(window: UIWindow) -> UIImage? {

    UIGraphicsBeginImageContextWithOptions(window.bounds.size, true, UIScreen.main.scale)

    guard let currentContext = UIGraphicsGetCurrentContext() else {
        return nil
    }

    window.layer.render(in: currentContext)
    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
        UIGraphicsEndImageContext()
        return nil
    }

    UIGraphicsEndImageContext()
    return image
}
jarora
  • 5,384
  • 2
  • 34
  • 46
4

I'm answering this question as it's a highly viewed, and there are many answers out there plus there's Swift and Obj-C.

Disclaimer This is not my code, nor my answers, this is only to help people that land here find a quick answer. There are links to the original answers to give credit where credit is due!! Please honor the original answers with a +1 if you use their answer!


Using QuartzCore

#import <QuartzCore/QuartzCore.h> 

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
} else {
    UIGraphicsBeginImageContext(self.window.bounds.size);
}

[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData) {
    [imageData writeToFile:@"screenshot.png" atomically:YES];
} else {
    NSLog(@"error while taking screenshot");
}

In Swift

func captureScreen() -> UIImage
{

    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);

    self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)

    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return image
}

Note: As the nature with programming, updates may need to be done so please edit or let me know! *Also if I failed to include an answer/method worth including feel free to let me know as well!

Jab
  • 26,853
  • 21
  • 75
  • 114
  • 1
    This answer doesn't seem to add anything. You just copy-pasted two of the answers already on the page and posted the result as your own. – user2357112 Nov 23 '18 at 07:02
  • My intention was not to take credit for their answers, it was to include both swift and Obj-C in one answer so it was quicker for people to find. I referred to the corresponding answers as well. if this should not be done I will delete my answer and revert back to just one of them. – Jab Nov 23 '18 at 07:08
  • I am using the ObjC solution. For me, I had to add ".view" after "self", but the result is 100%. This is GOLD – samouray Sep 24 '19 at 15:01
  • It worked for Capturing Presentation Stream also. Thanks – YSR fan Apr 14 '20 at 07:57
4

This will work with swift 4.2, the screenshot will be saved in library, but please don't forget to edit the info.plist @ NSPhotoLibraryAddUsageDescription :

  @IBAction func takeScreenshot(_ sender: UIButton) {

    //Start full Screenshot
    print("full Screenshot")
    UIGraphicsBeginImageContext(card.frame.size)
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    var sourceImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    UIImageWriteToSavedPhotosAlbum(sourceImage!, nil, nil, nil)

    //Start partial Screenshot
    print("partial Screenshot")
    UIGraphicsBeginImageContext(card.frame.size)
    sourceImage?.draw(at: CGPoint(x:-25,y:-100)) //the screenshot starts at -25, -100
    var croppedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    UIImageWriteToSavedPhotosAlbum(croppedImage!, nil, nil, nil)

}
sayhan
  • 1,168
  • 1
  • 16
  • 22
Just Lenz
  • 41
  • 1
3

For iOS 7.0 or above..

If you want to take screenshots of view say(myView), you can do this with single line:

[myView snapshotViewAfterScreenUpdates:NO];
2

Get Screenshot From View

-(UIImage *)getScreenshotImage {
    if ([[UIScreen mainScreen] scale] == 2.0) {
        UIGraphicsBeginImageContextWithOptions(self.view.frame.size, FALSE, 2.0);
    } else {
        UIGraphicsBeginImageContextWithOptions(self.view.frame.size, FALSE, 1.0);
    }

    [self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return result;
}

Save Image to Photos

UIImageWriteToSavedPhotosAlbum(YOUR_IMAGE, nil, nil, nil);

How-To

UIImageWriteToSavedPhotosAlbum([self getScreenshotImage], nil, nil, nil);
Fernando Cervantes
  • 2,962
  • 2
  • 23
  • 34
2

Two options available at bellow site:

OPTION 1: using UIWindow (tried and work perfectly)

// create graphics context with screen size
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);

// grab reference to our window
UIWindow *window = [UIApplication sharedApplication].keyWindow;

// transfer content into our context
[window.layer renderInContext:ctx];
UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

OPTION 2: using UIView

// grab reference to the view you'd like to capture
UIView *wholeScreen = self.splitViewController.view;

// define the size and grab a UIImage from it
UIGraphicsBeginImageContextWithOptions(wholeScreen.bounds.size, wholeScreen.opaque, 0.0);
[wholeScreen.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

For retina screen (as DenNukem answer)

// grab reference to our window
    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    // create graphics context with screen size
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, [UIScreen mainScreen].scale);
    } else {
        UIGraphicsBeginImageContext(screenRect.size);
        [window.layer renderInContext:UIGraphicsGetCurrentContext()];
    }

for more detail: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/

Nhat Dinh
  • 3,378
  • 4
  • 35
  • 51
2

Get Screenshot From View :

- (UIImage *)takeSnapshotView {
    CGRect rect = [myView bounds];//Here you can change your view with myView
    UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [myView.layer renderInContext:context];
    UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return capturedScreen;//capturedScreen is the image of your view
}

Hope, this is what you're looking for. Any concern get back to me. :)

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
1

Another option is to use the Automation tool on instruments. You write a script to put the screen into whatever you state you want, then take the shots. Here is the script I used for one of my apps. Obviously, the details of the script will be different for your app.

var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
var picker = window.pickers()[0];
var wheel = picker.wheels()[2];
var buttons = window.buttons();
var button1 = buttons.firstWithPredicate("name == 'dateButton1'");
var button2 = buttons.firstWithPredicate("name == 'dateButton2'");

function setYear(picker, year) {
    var yearName = year.toString();
    var yearWheel = picker.wheels()[2];
    yearWheel.selectValue(yearName);
}

function setMonth(picker, monthName) {
    var wheel = picker.wheels()[0];
    wheel.selectValue(monthName);
}

function setDay(picker, day) {
    var wheel = picker.wheels()[1];
    var name = day.toString();
    wheel.selectValue(name);
}

target.delay(1);
setYear(picker, 2015);
setMonth(picker, "July");
setDay(picker, 4);
button1.tap();
setYear(picker, 2015);
setMonth(picker, "December");
setDay(picker, 25);

target.captureScreenWithName("daysShot1");

var nButtons = buttons.length;
UIALogger.logMessage(nButtons + " buttons");
for (var i=0; i<nButtons; i++) {
    UIALogger.logMessage("button " + buttons[i].name());
}

var tabBar = window.tabBars()[0];
var barButtons = tabBar.buttons();

var nBarButtons = barButtons.length;
UIALogger.logMessage(nBarButtons + " buttons on tab bar");

for (var i=0; i<nBarButtons; i++) {
    UIALogger.logMessage("button " + barButtons[i].name());
}

var weeksButton = barButtons[1];
var monthsButton = barButtons[2];
var yearsButton = barButtons[3];

target.delay(2);
weeksButton.tap();
target.captureScreenWithName("daysShot2");
target.delay(2);
monthsButton.tap();
target.captureScreenWithName("daysShot3");
target.delay(2);
yearsButton.tap();
target.delay(2);
button2.tap();
target.delay(2);
setYear(picker, 2018);
target.delay(2);
target.captureScreenWithName("daysShot4");
William Jockusch
  • 26,513
  • 49
  • 182
  • 323
1

Just a small contribution, I've done this with a button but the pressing also means the button is captured pressed. So first I unhighlight.

- (IBAction)screenShot:(id)sender {
    // Unpress screen shot button
    screenShotButton.highlighted = NO;

    // create graphics context with screen size
    CGRect screenRect = [[UIScreen mainScreen] bounds];

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, [UIScreen mainScreen].scale);
    } else {
        UIGraphicsBeginImageContext(self.view.bounds.size);
    }

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [[UIColor blackColor] set];
    CGContextFillRect(ctx, screenRect);

    // grab reference to our window
    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    // transfer content into our context
    [window.layer renderInContext:ctx];
    UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // save screengrab to Camera Roll
    UIImageWriteToSavedPhotosAlbum(screengrab, nil, nil, nil);
}

I got the main body of the code from: http://pinkstone.co.uk/how-to-take-a-screeshot-in-ios-programmatically/ where I used option 1, option 2 didn't seem to work for me. Added the adjustments for Rentina screen sizes from this thread, and the unhighlighting of the screenShotButton. The view I'm using it on is a StoryBoarded screen of buttons and labels and with several UIView added later via the program.

TheQ
  • 6,858
  • 4
  • 35
  • 55
Symanski
  • 337
  • 3
  • 11
  • Thanks the_UB for the edit. I'd realised I'd left in commented code, but then thought "that's how genuine the pasted code is". Live working code which is now in the App Store. – Symanski May 07 '15 at 07:49
1

In Swift you can use following code.

if UIScreen.mainScreen().respondsToSelector(Selector("scale")) {
    UIGraphicsBeginImageContextWithOptions(self.window!.bounds.size, false, UIScreen.mainScreen().scale)
}
else{
    UIGraphicsBeginImageContext(self.window!.bounds.size)
}
self.window?.layer.renderInContext(UIGraphicsGetCurrentContext())
var image : UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Mohammad Sadiq Shaikh
  • 3,160
  • 9
  • 35
  • 54
0

Swift 4:

func makeImage(withView view: UIView) -> UIImage? {

    let rect = view.bounds

    UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)

    guard let context = UIGraphicsGetCurrentContext() else {
      assertionFailure()
      return nil
    }

    view.layer.render(in: context)

    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
      assertionFailure()
      return nil
    }

    UIGraphicsEndImageContext()

    return image
}
Chris Livdahl
  • 4,662
  • 2
  • 38
  • 33