uiimage uikit scale retina-display image-capture

Cómo capturar UIView a UIImage sin pérdida de calidad en la pantalla retina



uikit scale (15)

Mi código funciona bien para dispositivos normales pero crea imágenes borrosas en dispositivos de retina.

¿Alguien sabe una solución para mi problema?

+ (UIImage *) imageWithView:(UIView *)view { UIGraphicsBeginImageContext(view.bounds.size); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }


Swift 3

La solución Swift 3 (basada en share ) con la extensión UIView debería ser así:

extension UIView { public func getSnapshotImage() -> UIImage { UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0) self.drawHierarchy(in: self.bounds, afterScreenUpdates: false) let snapshotImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return snapshotImage } }


Implementación Swift 3.0

extension UIView { func getSnapshotImage() -> UIImage { UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0) drawHierarchy(in: bounds, afterScreenUpdates: false) let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return snapshotImage } }


Agregue esto al método para UIView Categoría

- (UIImage*) capture { UIGraphicsBeginImageContext(self.bounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); [self.layer renderInContext:context]; UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }



Aquí hay una extensión Swift 4 UIView basada en la respuesta de @Dima.

extension UIView { func image () -> UIImage? { UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0) drawHierarchy(in: bounds, afterScreenUpdates: false) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } }


Cambie del uso de UIGraphicsBeginImageContext a UIGraphicsBeginImageContextWithOptions (como se documenta en esta página ). Pase 0.0 por la escala (el tercer argumento) y obtendrá un contexto con un factor de escala igual al de la pantalla.

UIGraphicsBeginImageContext utiliza un factor de escala fijo de 1.0, por lo que en realidad está obteniendo exactamente la misma imagen en un iPhone 4 que en otros iPhones. Apuesto a que el iPhone 4 está aplicando un filtro cuando lo escalas implícitamente o simplemente tu cerebro está detectando que es menos afilado que todo lo que lo rodea.

Entonces, supongo

#import <QuartzCore/QuartzCore.h> + (UIImage *)imageWithView:(UIView *)view { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }

Y en el veloz 4:

func image(with view: UIView) -> UIImage? { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) defer { UIGraphicsEndImageContext() } if let context = UIGraphicsGetCurrentContext() { view.layer.render(in: context) let image = UIGraphicsGetImageFromCurrentImageContext() return image } return nil }


En este método, simplemente pase un objeto de vista y devolverá un objeto UIImage.

-(UIImage*)getUIImageFromView:(UIView*)yourView { UIGraphicsBeginImageContext(yourView.bounds.size); [yourView.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }


He creado una extensión Swift basada en la solución @Dima:

extension UIImage { class func imageWithView(view: UIView) -> UIImage { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0) view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true) let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img } }

EDITAR: Swift 4 versión mejorada

extension UIImage { class func imageWithView(_ view: UIView) -> UIImage { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0) defer { UIGraphicsEndImageContext() } view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() } }

Uso:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) let image = UIImage.imageWithView(view)


La respuesta aceptada actual está desactualizada, al menos si es compatible con iOS 7.

Esto es lo que debería usar si solo es compatible con iOS7 +:

+ (UIImage *) imageWithView:(UIView *)view { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f); [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO]; UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return snapshotImage; }

Swift 4:

func imageWithView(view: UIView) -> UIImage? { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) let snapshotImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return snapshotImage }

Según este artículo , puede ver que el nuevo método iOS7 drawViewHierarchyInRect:afterScreenUpdates: es muchas veces más rápido que renderInContext:


Para mejorar las respuestas de @Tommy y @Dima, use la siguiente categoría para convertir UIView en UIImage con un fondo transparente y sin pérdida de calidad. Trabajando en iOS7. (O simplemente reutilice ese método en la implementación, reemplazando la self referencia con su imagen)

UIView + RenderViewToImage.h

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

UIView + RenderViewToImage.m

#import "UIView+RenderViewToImage.h" @implementation UIView (RenderViewToImage) - (UIImage *)imageByRenderingView { UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0); [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return snapshotImage; } @end


Swift 2.0:

Usando el método de extensión:

override func viewDidLoad() { super.viewDidLoad() //Sample View To Self.view let sampleView = UIView(frame: CGRectMake(100,100,200,200)) sampleView.backgroundColor = UIColor(patternImage: UIImage(named: "ic_120x120")!) self.view.addSubview(sampleView) //ImageView With Image let sampleImageView = UIImageView(frame: CGRectMake(100,400,200,200)) //sampleView is rendered to sampleImage var sampleImage = UIImage.renderUIViewToImage(sampleView) sampleImageView.image = sampleImage self.view.addSubview(sampleImageView) }

Uso:

extension UIView { public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage? { if #available(iOS 10.0, *) { let rendererFormat = UIGraphicsImageRendererFormat.default() rendererFormat.scale = self.layer.contentsScale rendererFormat.opaque = self.isOpaque let renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: rendererFormat) return renderer.image { _ in self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates) } } else { UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, self.layer.contentsScale) defer { UIGraphicsEndImageContext() } self.drawHierarchy(in: self.bounds, afterScreenUpdates: afterScreenUpdates) return UIGraphicsGetImageFromCurrentImageContext() } } }


Todas las respuestas de Swift 3 no funcionaron para mí, así que he traducido la respuesta más aceptada:

extension UIImage { class func imageWithView(view: UIView) -> UIImage { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) view.layer.render(in: UIGraphicsGetCurrentContext()!) let img: UIImage? = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img! } }


Extensión Swift 3.0 Drop-in que admite la nueva API de iOS 10.0 y el método anterior.

Nota:

  • Comprobación de la versión de iOS
  • Tenga en cuenta el uso de diferir para simplificar la limpieza del contexto.
  • También se aplicará la opacidad y escala actual de la vista.
  • ¡Nada es solo desenvuelto usando ! lo que podría causar un accidente.

extension UIImage{ class func renderUIViewToImage(viewToBeRendered:UIView?) -> UIImage { UIGraphicsBeginImageContextWithOptions((viewToBeRendered?.bounds.size)!, false, 0.0) viewToBeRendered!.drawViewHierarchyInRect(viewToBeRendered!.bounds, afterScreenUpdates: true) viewToBeRendered!.layer.renderInContext(UIGraphicsGetCurrentContext()!) let finalImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return finalImage } }


Usando el UIGraphicsImageRenderer moderno

public extension UIView { @available(iOS 10.0, *) public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage { let rendererFormat = UIGraphicsImageRendererFormat.default() rendererFormat.opaque = isOpaque let renderer = UIGraphicsImageRenderer(size: bounds.size, format: rendererFormat) let snapshotImage = renderer.image { _ in drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates) } return snapshotImage } }


- (UIImage*)screenshotForView:(UIView *)view { UIGraphicsBeginImageContext(view.bounds.size); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // hack, helps w/ our colors when blurring NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg image = [UIImage imageWithData:imageData]; return image; }