iOS: ¿cuál es la manera más rápida y más efectiva de hacer una captura de pantalla mediante progtwigción?

en mi aplicación de iPad, me gustaría hacer una captura de pantalla de una UIView tomando una gran parte de la pantalla. Desafortunadamente, las subvistas están muy anidadas, por lo que lleva mucho time hacer la captura de pantalla y animar una página a curvarse después.

¿Hay una manera más rápida que la "usual"?

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

Si es posible, me gustaría evitar el almacenamiento en caching o la reestructuración de mi vista.

Encontré un método mejor que utiliza la API de instantáneas siempre que sea posible.

Espero que ayude.

 class func screenshot() -> UIImage { var imageSize = CGSize.zero let orientation = UIApplication.shanetworking.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.shanetworking.windows { window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) } let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! } 

¿Quieres saber más sobre las instantáneas de iOS 7?

Versión Objective-C:

 + (UIImage *)screenshot { CGSize imageSize = CGSizeZero; UIInterfaceOrientation orientation = [UIApplication shanetworkingApplication].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 shanetworkingApplication] 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 3 de octubre de 2013 Actualizado para admitir el nuevo súper rápido drawViewHierarchyInRect: afterScreenUpdates: método en iOS 7.


No. Render renderIncontext: es hasta donde sé que la única forma de hacerlo. Podría crear una categoría de UIView como esta, para hacerlo más fácil para usted en el futuro:

UIView + Captura de pantalla.h

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

UIView + Captura de pantalla.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 

Con esto, puede decir [self.view.window imageRepresentation] en un controller de vista y get una captura de pantalla completa de su aplicación. Sin embargo, esto podría excluir la barra de estado.

EDITAR:

Y puedo agregar. Si tiene una UIView con contenido transparente y necesita una representación de image CON el contenido subyacente también, puede tomar una representación de image de la vista de contenedor y recortar esa image, simplemente tomando el rectángulo de la subvista y convirtiéndolo en el contenedor Vistas del sistema de coorderadas.

 [view convertRect:self.bounds toView:containerView] 

Para recortar, vea la respuesta a esta pregunta: Recortar un UIImage

iOS 7 introdujo un nuevo método que le permite dibujar una jerarquía de vista en el context de charts actual. Esto se puede usar para get un UIImage muy rápido.

Implementado como método de categoría en UIView :

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

Es considerablemente más rápido que el método renderInContext: existente.

ACTUALIZACIÓN PARA SWIFT : una extensión que hace lo mismo:

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

Combiné las respuestas a la function única que se ejecutará para cualquier versión de iOS, incluso para retina o dispositivos que no retienen.

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

Lo que está pidiendo como alternativa es leer la GPU (ya que la pantalla está compuesta de cualquier número de vistas translúcidas), que también es una operación inherentemente lenta.

Para mí, establecer la calidad de interpolación fue un largo path.

 CGContextSetInterpolationQuality(ctx, kCGInterpolationNone); 

Si está capturando imágenes muy detalladas, esta solución puede no ser aceptable. Si está capturando un text, apenas notará la diferencia.

Esto networkingujo el time para tomar el tiro instantáneo de manera significativa, así como hacer una image que consumía mucha less memory.

Esto sigue siendo beneficioso con drawViewHierarchyInRect: afterScreenUpdates: method.