Je souhaite convertir une image UIView en une image et la sauvegarder dans mon application. Quelqu'un peut-il me dire s'il vous plaît comment prendre une capture d'écran d'une vue ou la convertir en une image et quel est le meilleur moyen de l'enregistrer dans une application (Pas de pellicule) Voici le code pour la vue:
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)
Par exemple si j'ai une vue en taille: 50 50 à 100,100. Je peux utiliser ce qui suit pour prendre une capture d'écran:
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();
Une extension sur UIView
devrait faire l'affaire.
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 déconseille d'utiliser UIGraphicsBeginImageContext
à partir de iOS 10 avec l'introduction de la gamme de couleurs P3. UIGraphicsBeginImageContext
est sRGB et 32 bits uniquement. Ils ont présenté la nouvelle API UIGraphicsImageRenderer
entièrement gérée par la couleur, basée sur des blocs, comportant des sous-classes pour les PDF et les images, et gérant automatiquement la durée de vie du contexte. Voir WWDC16 session 205 pour plus de détails (le rendu de l'image commence aux alentours de 11h50)
Edit1: pour être sûr que cela fonctionne sur tous les appareils, utilisez #available
avec un repli sur les versions antérieures d'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!)
}
}
}
vous pouvez utiliser l'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!)
}
}
Voici la version Swift 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!)
}
}
Convertissez votre UIView en image par drawViewHierarchyInRect: afterScreenUpdates: qui est plusieurs fois plus rapide que renderInContext
Remarque importante: n'appelez pas cette fonction depuis viewDidLoad ou viewWillAppear, assurez-vous de capturer une vue après son affichage/chargement complet
Obj C
UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f);
[myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO];
UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myImageView.image = snapshotImageFromMyView;
Enregistrer l'image modifiée Album photo
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
Généralisation super facile avec extension, 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)
Sur 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)!)
}
}
UIGraphicsBeginImageContext(self.view.bounds.size);
self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
var screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
tout en prenant en charge iOS 9 et les versions antérieures
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()
}
}
}
Je ne sais pas ce que la question veut dire par:
quel est le meilleur moyen de l'enregistrer dans une application (pas de pellicule)?
À mon avis, l'approche avec l'initialiseur n'est pas géniale car elle crée deux images.
Je préfère ça:
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
}
}
var snapshot = overView.snapshotViewAfterScreenUpdates(false)
ou en objectif-c
UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];
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) }
}
}
Échantillon
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)
Vous pouvez l'utiliser facilement en utilisant l'extension comme ceci
// 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 vous voulez utiliser ces méthodes/variables d'extension, implémentez ceci
Extension UIImage
extension UIImage {
convenience init(view: UIView) {
if let cgImage = view.snapshot?.cgImage {
self.init(cgImage: cgImage)
} else {
self.init()
}
}
}
Extension 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
}
}
Extension UIApplication
extension UIApplication {
var snapshot: UIImage? {
return keyWindow?.rootViewController?.view.snapshot
}
}
Cela fonctionne pour moi pour Xcode 9/Swift 3.2/Swift 4 et 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!
}
Voici comment l'utiliser dans une fonction:
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!
}
}
Voici comment faire quelque chose avec l'image renvoyée par la fonction:
@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!
}
J'ai eu la réponse Xcode 9/Swift 3.2/Swift 4 de Paul Hudson convertir uiview en uiimage
J'ai eu le Xcode 8/Swift 3 de quelque part sur SO il y a longtemps et j'ai oublié où :(
ou iOS 10+, vous pouvez utiliser le nouveau UIGraphicsImageRenderer + la drawHierarchy recommandée, qui peut parfois être beaucoup plus rapide 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)
}
}
}
Mise en oeuvre dans Swift 3 :
Ajoutez le code ci-dessous, hors de la portée de la classe.
extension UIImage {
convenience init(_ view: UIView) {
UIGraphicsBeginImageContext(view.frame.size)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: (image?.cgImage)!)
}
}
Utilisation:
let image = UIImage( Your_View_Outlet )
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)!)
}
}
en utilisant:
let img = UIImage.init (vue: self.holderView)
Merci @Bao Tuan Diep! Je veux ajouter un supplément.
Lorsque vous utilisez le code:
yourView.layer.render(in:UIGraphicsGetCurrentContext()!)
Vous devez remarquer 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.
Ensuite, vous devez utiliser :
[yourView setNeedsLayout];
[yourView layoutIfNeeded];
mettre à jour yourView
avant yourView.layer.render(in:UIGraphicsGetCurrentContext()!)
.
Sinon, vous pouvez obtenir un objet image ne contenant aucun élément
fonctionne bien avec moi!
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!
}
}
Initialiseur avec le nouveau UIGraphicsImageRenderer disponible depuis 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)!)
}
}
J'ai implémenté la méthode de -Naveed J. comme ceci, et cela a fonctionné comme un charme.
Voici son extension:
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) } } }
Voici comment je l'ai implémenté.
//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()
La vue contient une sous-vue floue (par exemple UIVisualEffectView instance), seul drawViewHierarchyInRect: afterScreenUpdates fonctionne.
La réponse de @ViJay Avhad est correcte pour ce cas.