iOS:以编程方式制作截图的最快速,最高效的方式是什么?

在我的iPad应用程序中,我想制作一个占用屏幕大部分的UIView屏幕截图。 不幸的是,子视图是非常深的嵌套,所以需要很长的时间才能制作屏幕截图并在之后卷动页面。

有没有比“平常”更快的方法?

UIGraphicsBeginImageContext(self.bounds.size); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

如果可能的话,我想避免caching或重组我的观点。

我发现一个更好的方法,尽可能使用快照API。

我希望它有帮助。

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

想知道更多关于iOS 7快照吗?

Objective-C版本:

 + (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; } 

编辑10月3日2013年更新,以支持iOS 7中新的超级快速drawViewHierarchyInRect:afterScreenUpdates:方法。


不,CALayer的renderInContext:据我所知是唯一的方法来做到这一点。 你可以像这样创build一个UIView类别,让自己更容易前进:

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 

通过这个,你可以在视图控制器中说[self.view.window imageRepresentation] ,并获得你的应用程序的完整截图。 这可能会排除状态栏。

编辑:

我可以补充一下。 如果你有一个带有透明内容的UIView,并且需要一个带有底层内容的图像表示,那么你可以抓取容器视图的图像表示并裁剪该图像,只需要将子视图的矩形转换为容器视图坐标系。

 [view convertRect:self.bounds toView:containerView] 

裁剪看到这个问题的答案: 裁剪UIImage

iOS 7引入了一种新的方法,允许您将视图层次结构绘制到当前的graphics上下文中。 这可以用来快速获取UIImage

UIView上作为类别方法实现:

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

这比现有的renderInContext:方法快得多。

更新SWIFT :一个扩展,做同样的事情:

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

我将这些答案结合在一起,可以在任何iOS版本上运行,即使是在视网膜或非保留设备上。

 - (UIImage *)screenShot { if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, [UIScreen mainScreen].scale); else UIGraphicsBeginImageContext(self.view.bounds.size); #ifdef __IPHONE_7_0 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; #endif #else [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; #endif UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } 

你所要求的作为替代方法是读取GPU(因为屏幕是从任意数目的半透明视图合成的),这也是一个本质上缓慢的操作。

对于我来说,设置InterpolationQuality走了很长的路。

 CGContextSetInterpolationQuality(ctx, kCGInterpolationNone); 

如果您正在拍摄非常详细的图像,则此解决scheme可能不被接受。 如果你正在快照文本,你几乎不会注意到其中的差异。

这样可以大大缩短拍摄时间,同时也可以制作出消耗更less记忆的图像。

这仍然有利于drawViewHierarchyInRect:afterScreenUpdates:方法。