Como capturar o UIView para o UIImage sem perda de qualidade na exibição de retina

Meu código funciona bem para dispositivos normais, mas cria imagens borradas em dispositivos de retina.

Alguém conhece uma solução para o meu problema?

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

Alterne do uso de UIGraphicsBeginImageContext para UIGraphicsBeginImageContextWithOptions (conforme documentado nesta página ). Passe 0,0 para escala (o terceiro argumento) e você terá um contexto com um fator de escala igual ao da canvas.

UIGraphicsBeginImageContext usa um fator de escala fixo de 1.0, então você está realmente obtendo exatamente a mesma imagem em um iPhone 4 como nos outros iPhones. Eu aposto que o iPhone 4 está aplicando um filtro quando você aumenta implicitamente ou apenas o seu cérebro está percebendo que ele é menos afiado do que tudo ao seu redor.

Então eu acho:

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

E no rápido 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 } 

A resposta atual aceita está desatualizada, pelo menos se você estiver suportando o iOS 7.

Aqui está o que você deve usar se estiver apenas suportando o 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; } 

De acordo com este artigo , você pode ver que o novo método drawViewHierarchyInRect:afterScreenUpdates: é muitas vezes mais rápido que renderInContext: referência

Eu criei uma extensão Swift baseada na solução @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 } } 

EDIT: Swift 4 versão melhorada

 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) 

Para melhorar as respostas de @ Tommy e @Dima, use a seguinte categoria para renderizar o UIView para o UIImage com fundo transparente e sem perda de qualidade. Trabalhando no iOS7. (Ou apenas reutilize esse método na implementação, substituindo self referência self pela sua imagem)

UIView + RenderViewToImage.h

 #import  @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 3

A solução Swift 3 (com base na resposta de Dima ) com a extensão UIView deve ser assim:

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

iOSRápido

Usando o 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 } } 

Swift 2.0:

Usando o método de extensão:

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

Uso:

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

Extensão do Drop-in Swift 3.0 que suporta a nova API do iOS 10.0 e o método anterior.

Nota:

  • verificação de versão do iOS
  • Observe o uso de adiar para simplificar a limpeza de contexto.
  • Também aplicará a opacidade e a escala atual da exibição.
  • Nada é apenas desembrulhado usando ! o que poderia causar um acidente.

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

Implementação do Swift 3.0

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

Todas as respostas do Swift 3 não funcionaram para mim, então traduzi a resposta mais aceita:

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

Algumas vezes o método drawRect faz o problema, então eu obtive essas respostas mais apropriadas. Você também pode dar uma olhada nele Capture UIImage of UIView preso no método DrawRect

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

Aqui está uma extensão do Swift 4 UIView baseada na resposta do @Dima.

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

Neste método, basta passar um object view e ele retornará um object UIImage.

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

Adicione isto ao método para a categoria UIView

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