iOS: what's the fastest, most performant way to make a screenshot programmatically? iOS: what's the fastest, most performant way to make a screenshot programmatically? ios ios

iOS: what's the fastest, most performant way to make a screenshot programmatically?


I've found a better method that uses the snapshot API whenever possible.

I hope it helps.

class func screenshot() -> UIImage {    var imageSize = CGSize.zero    let orientation = UIApplication.shared.statusBarOrientation    if UIInterfaceOrientationIsPortrait(orientation) {        imageSize = UIScreen.main.bounds.size    } else {        imageSize = CGSize(width: UIScreen.main.bounds.size.height, height: UIScreen.main.bounds.size.width)    }    UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)    for window in UIApplication.shared.windows {        window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)    }    let image = UIGraphicsGetImageFromCurrentImageContext()    UIGraphicsEndImageContext()    return image!}

Wanna know more about iOS 7 Snapshots?

Objective-C version:

+ (UIImage *)screenshot{    CGSize imageSize = CGSizeZero;    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;    if (UIInterfaceOrientationIsPortrait(orientation)) {        imageSize = [UIScreen mainScreen].bounds.size;    } else {        imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);    }    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);    CGContextRef context = UIGraphicsGetCurrentContext();    for (UIWindow *window in [[UIApplication sharedApplication] windows]) {        CGContextSaveGState(context);        CGContextTranslateCTM(context, window.center.x, window.center.y);        CGContextConcatCTM(context, window.transform);        CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);        if (orientation == UIInterfaceOrientationLandscapeLeft) {            CGContextRotateCTM(context, M_PI_2);            CGContextTranslateCTM(context, 0, -imageSize.width);        } else if (orientation == UIInterfaceOrientationLandscapeRight) {            CGContextRotateCTM(context, -M_PI_2);            CGContextTranslateCTM(context, -imageSize.height, 0);        } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {            CGContextRotateCTM(context, M_PI);            CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);        }        if ([window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {            [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];        } else {            [window.layer renderInContext:context];        }        CGContextRestoreGState(context);    }    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return image;}



EDIT October 3. 2013Updated to support the new super fast drawViewHierarchyInRect:afterScreenUpdates: method in iOS 7.


No. CALayer's renderInContext: is as far as I know the only way to do this. You could create a UIView category like this, to make it easier for yourself going forward:

UIView+Screenshot.h

#import <UIKit/UIKit.h>@interface UIView (Screenshot)- (UIImage*)imageRepresentation;@end

UIView+Screenshot.m

#import <QuartzCore/QuartzCore.h>#import "UIView+Screenshot.h"@implementation UIView (Screenshot)- (UIImage*)imageRepresentation {    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);    /* iOS 7 */    if ([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])                    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];    else /* iOS 6 */        [self.layer renderInContext:UIGraphicsGetCurrentContext()];    UIImage* ret = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return ret;}@end

By this you might be able to say [self.view.window imageRepresentation] in a view controller, and get a full screenshot of your app. This might exclude the statusbar though.

EDIT:

And may I add. If you have an UIView with transparent content, and needs an image representation WITH the underlaying content as well, you can grab an image representation of the container view and crop that image, simply by taking the rect of the subview and converting it to the container views coordinate system.

[view convertRect:self.bounds toView:containerView]

To crop see answer to this question: Cropping an UIImage


iOS 7 introduced a new method that allows you to draw a view hierarchy into the current graphics context. This can be used to get an UIImage very fast.

Implemented as category method on UIView:

- (UIImage *)pb_takeSnapshot {    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return image;}

It is considerably faster then the existing renderInContext: method.

UPDATE FOR SWIFT: An extension that does the same:

extension UIView {    func pb_takeSnapshot() -> UIImage {        UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.mainScreen().scale);        self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)        // old style: self.layer.renderInContext(UIGraphicsGetCurrentContext())        let image = UIGraphicsGetImageFromCurrentImageContext();        UIGraphicsEndImageContext();        return image;    }}