ios swift uiview screenshot

ios - Cómo convertir una vista UIView en una imagen



swift screenshot (21)

Mejores prácticas a partir de iOS 10 y Swift 3

sin dejar de admitir iOS 9 y versiones anteriores

extension UIView { func asImage() -> UIImage? { if #available(iOS 10.0, *) { let renderer = UIGraphicsImageRenderer(bounds: bounds) return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) } } else { UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0) defer { UIGraphicsEndImageContext() } guard let currentContext = UIGraphicsGetCurrentContext() else { return nil } self.layer.render(in: currentContext) return UIGraphicsGetImageFromCurrentImageContext() } } }

No estoy seguro de qué significa la pregunta por:

¿Cuál es la mejor manera de guardarlo en una aplicación (no en el carrete de la cámara)?

Quiero convertir una vista UIView en una imagen y guardarla en mi aplicación. ¿Alguien puede decirme cómo tomar una captura de pantalla de una vista o convertirla en una imagen y cuál es la mejor manera de guardarla en una aplicación (no en el carrete de la cámara)? Aquí está el código para la vista:

var overView = UIView(frame: CGRectMake(0, 0, self.view.frame.width/1.3, self.view.frame.height/1.3)) overView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)-self.view.frame.height/16); overView.backgroundColor = UIColor.whiteColor() self.view.addSubview(overView) self.view.bringSubviewToFront(overView)


Convierta su UIView en imagen mediante drawViewHierarchyInRect: afterScreenUpdates: que es muchas veces más rápido que renderInContext

Nota importante: no llame a esta función desde viewDidLoad o viewWillAppear , asegúrese de capturar una vista después de que se muestre / cargue completamente

Obj C

UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f); [myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO]; UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); myImageView.image = snapshotImageFromMyView;

Guardar la imagen editada Álbum de fotos

UIImageWriteToSavedPhotosAlbum(snapshotImageFromMyView, nil,nil, nil);

Swift 3/4

UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.isOpaque, 0.0) myView.drawHierarchy(in: myView.bounds, afterScreenUpdates: false) let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() print(snapshotImageFromMyView) myImageView.image = snapshotImageFromMyView

Generalización súper fácil con extensión, iOS11, swift3 / 4

extension UIImage{ convenience init(view: UIView) { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) view.drawHierarchy(in: view.bounds, afterScreenUpdates: false) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: (image?.cgImage)!) } } Use : //myView is completly loaded/visible , calling this code after only after viewDidAppear is call imgVV.image = UIImage.init(view: myView) // Simple image object let img = UIImage.init(view: myView)


En iOS 10:

extension UIImage { convenience init(view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: (image?.cgImage)!) } }


En mi opinión, el enfoque con el inicializador no es tan bueno porque crea dos imágenes.

Prefiero esto:

extension UIView { var snapshot: UIImage? { UIGraphicsBeginImageContext(self.frame.size) guard let context = UIGraphicsGetCurrentContext() else { return nil } layer.render(in: context) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } }


Esto funciona para mí para Xcode 9 / Swift 3.2 / Swift 4 y Xcode 8 / Swift 3

if #available(iOS 10.0, *) { // for Xcode 9/Swift 3.2/Swift 4 -Paul Hudson''s code let renderer = UIGraphicsImageRenderer(size: view!.bounds.size) let capturedImage = renderer.image { (ctx) in view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true) } return capturedImage } else { // for Xcode 8/Swift 3 UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0) view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false) let capturedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return capturedImage! }

Aquí se explica cómo usarlo dentro de una función:

fileprivate func captureUIImageFromUIView(_ view:UIView?) -> UIImage { guard (view != nil) else{ // if the view is nil (it''s happened to me) return an alternative image let errorImage = UIImage(named: "Error Image") return errorImage } // if the view is all good then convert the image inside the view to a uiimage if #available(iOS 10.0, *) { let renderer = UIGraphicsImageRenderer(size: view!.bounds.size) let capturedImage = renderer.image { (ctx) in view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true) } return capturedImage } else { UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0) view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false) let capturedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return capturedImage! } }

Aquí se explica cómo hacer algo con la imagen devuelta por la función:

@IBOutlet weak fileprivate var myCustomView: UIView! var myPic: UIImage? let myImageView = UIImageView() @IBAction fileprivate func saveImageButtonTapped(_ sender: UIButton) { myPic = captureUIImageFromUIView(myCustomView) // display the pic inside a UIImageView myImageView.image = myPic! }

hackingwithswift.com/example-code/media/… respuesta Xcode 9 / Swift 3.2 / Swift 4 de Paul Hudson hackingwithswift.com/example-code/media/…

Obtuve el Xcode 8 / Swift 3 de algún lugar en SO hace mucho tiempo y olvidé dónde :(


Gracias @Bao Tuan Diep! Quiero agregar un suplemento.

Cuando usas el código:

yourView.layer.render(in:UIGraphicsGetCurrentContext()!)

Debes notar que:

- If you had used `autoLayout` or `Masonry` in `yourView` (that you want to convert) . - If you did not add `yourView` to another view which means that `yourView` was not used as a subview but just an object.

Entonces, debes usar :

[yourView setNeedsLayout]; [yourView layoutIfNeeded];

para actualizar yourView antes de yourView.layer.render(in:UIGraphicsGetCurrentContext()!) .

De lo contrario, puede obtener un objeto de imagen que no contiene elementos


Implementé el esta manera, y funcionó de .

Aquí estaba su extensión:

extension UIView { // Using a function since `var image` might conflict with an existing variable // (like on `UIImageView`) func asImage() -> UIImage { let renderer = UIGraphicsImageRenderer(bounds: bounds) return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) } } }

Así es como lo implementé.

//create an image from yourView to display //determine the frame of the view/imageimage let screen = self.superview!.bounds let width = screen.width / 4 //make image 1/4 width of screen let height = width let frame = CGRect(x: 0, y: 0, width: width, height: height) let x = (screen.size.width - frame.size.width) * 0.5 let y = (screen.size.height - frame.size.height) * 0.5 let mainFrame = CGRect(x: x, y: y, width: frame.size.width, height: frame.size.height) let yourView = YourView() //instantiate yourView yourView.frame = mainFrame //give it the frame yourView.setNeedsDisplay() //tell it to display (I am not 100% sure this is needed) let characterViewImage = yourView.asImage()


Implementación en Swift 3 :

Agregue el código a continuación, fuera del alcance de la clase.

extension UIImage { convenience init(_ view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: (image?.cgImage)!) } }

Uso:

let image = UIImage( Your_View_Outlet )


Inicializador con el nuevo UIGraphicsImageRenderer disponible desde iOS 10:

extension UIImage{ convenience init(view: UIView) { let renderer = UIGraphicsImageRenderer(size: self.bounds.size) let canvas = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height) let image = renderer.image { _ in self.drawHierarchy(in: canvas, afterScreenUpdates: false) } self.init(cgImage: (image?.cgImage)!) } }


Para la vista contiene una subvista borrosa (por ejemplo, instancia UIVisualEffectView ), solo funciona drawViewHierarchyInRect: afterScreenUpdates .

La respuesta de @ViJay Avhad es correcta para este caso.


Por ejemplo, si tengo una vista de tamaño: 50 50 a 100,100. Puedo usar lo siguiente para tomar una captura de pantalla:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0); self.view.drawViewHierarchyInRect(CGRectMake(-50,-5-,view.bounds.size.width,view.bounds.size.height), afterScreenUpdates: true) var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();


Puede usarlo fácilmente usando la extensión como esta

// Take a snapshot from a view (just one view) let viewSnapshot = myView.snapshot // Take a screenshot (with every views in screen) let screenSnapshot = UIApplication.shared.snapshot // Take a snapshot from UIImage initialization UIImage(view: self.view)

Si quieres usar esos métodos / variables de extensión, implementa esto

  1. UIImage extension

    extension UIImage { convenience init(view: UIView) { if let cgImage = view.snapshot?.cgImage { self.init(cgImage: cgImage) } else { self.init() } } }

  2. Extensión UIView

    extension UIView { var snapshot: UIImage? { UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0) if UIGraphicsGetCurrentContext() != nil { drawHierarchy(in: bounds, afterScreenUpdates: true) let screenshot = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return screenshot } return nil } }

  3. Extensión de aplicación UIA

    extension UIApplication { var snapshot: UIImage? { return keyWindow?.rootViewController?.view.snapshot } }


Una extensión en UIView debería hacer el truco.

extension UIView { // Using a function since `var image` might conflict with an existing variable // (like on `UIImageView`) func asImage() -> UIImage { let renderer = UIGraphicsImageRenderer(bounds: bounds) return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) } } }

Apple desalienta el uso de UIGraphicsBeginImageContext partir de iOS 10 con la introducción de la gama de colores P3. UIGraphicsBeginImageContext es sRGB y solo de 32 bits. Presentaron la nueva API UIGraphicsImageRenderer que está completamente administrada en color, basada en bloques, tiene subclases para archivos PDF e imágenes, y gestiona automáticamente la vida útil del contexto. Consulte la sesión 205 de WWDC16 para obtener más detalles (la representación de la imagen comienza alrededor de la marca 11:50)

Edit1: para asegurarse de que funciona en todos los dispositivos, use #available con un #available a las versiones anteriores de iOS:

extension UIView { // Using a function since `var image` might conflict with an existing variable // (like on `UIImageView`) func asImage() -> UIImage { if #available(iOS 10.0, *) { let renderer = UIGraphicsImageRenderer(bounds: bounds) return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) } } else { UIGraphicsBeginImageContext(self.frame.size) self.layer.render(in:UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return UIImage(cgImage: image!.cgImage!) } } }


o iOS 10+ puedes usar el nuevo UIGraphicsImageRenderer + el drawHierarchy recomendado, que en algunas situaciones puede ser mucho más rápido que layer.renderInContext

extension UIView { func asImage() -> UIImage { let renderer = UIGraphicsImageRenderer(size: self.bounds.size) return renderer.image { _ in self.drawHierarchy(in: CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height), afterScreenUpdates: false) } } }


puedes usar extension

extension UIImage { convenience init(view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in:UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: image!.cgImage!) } }

Aquí la rápida versión 3/4:

extension UIImage { convenience init(view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in:UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: image!.cgImage!) } }


trabaja bien conmigo!

Swift4

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


Swift 4.2

import Foundation import UIKit extension UIImage { convenience init(view: UIView) { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0) view.drawHierarchy(in: view.bounds, afterScreenUpdates: false) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: (image?.cgImage)!) } }

utilizando:

let img = UIImage.init (ver: self.holderView)


Swift 4.2, iOS 10

extension UIView { // If Swift version is lower than 4.2, // You should change the name. (ex. var renderedImage: UIImage?) var image: UIImage? { let renderer = UIGraphicsImageRenderer(bounds: bounds) return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) } } }

Muestra

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) view.backgroundColor = .blue let view2 = UIView(frame: CGRect(x: 10, y: 10, width: 20, height: 20)) view2.backgroundColor = .red view.addSubview(view2) let imageView = UIImageView(image: view.image)


var snapshot = overView.snapshotViewAfterScreenUpdates(false)

o en el objetivo-c

UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];


UIGraphicsBeginImageContext(self.view.bounds.size); self.view.layer.renderInContext(UIGraphicsGetCurrentContext()) var screenShot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();


please try below code. -(UIImage *)getMainImageFromContext { UIGraphicsBeginImageContextWithOptions(viewBG.bounds.size, viewBG.opaque, 0.0); [viewBG.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }