web-dev-qa-db-fra.com

Comment capturer UIView vers UIImage sans perte de qualité sur l’affichage rétinien

Mon code fonctionne bien pour les périphériques normaux, mais crée des images floues sur les périphériques de la rétine.

Est-ce que quelqu'un connaît une solution à mon problème?

+ (UIImage *) imageWithView:(UIView *)view
{
    UIGraphicsBeginImageContext(view.bounds.size);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}
290
Daniel

Passez de l’utilisation de UIGraphicsBeginImageContext à UIGraphicsBeginImageContextWithOptions (comme documenté sur cette page ). Passez 0.0 pour scale (le troisième argument) et vous obtiendrez un contexte avec un facteur d'échelle égal à celui de l'écran.

UIGraphicsBeginImageContext utilise un facteur d'échelle fixe de 1,0; vous obtenez donc exactement la même image sur un iPhone 4 que sur les autres iPhones. Je parie que l'iPhone 4 applique un filtre lorsque vous le redimensionnez implicitement ou que votre cerveau le détecte moins net que tout ce qui l'entoure.

Donc je suppose:

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

Et dans Swift 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
}
651
Tommy

La réponse actuellement acceptée est maintenant obsolète, du moins si vous prenez en charge iOS 7.

Voici ce que vous devriez utiliser si vous ne supportez que 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)
    defer { UIGraphicsEndImageContext() }
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
    return UIGraphicsGetImageFromCurrentImageContext()
}

Selon cet article , vous pouvez voir que la nouvelle méthode iOS7 drawViewHierarchyInRect:afterScreenUpdates: est plusieurs fois plus rapide que renderInContext:benchmark

210
Dima

J'ai créé une extension Swift basée sur la solution @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 version améliorée

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

Usage:

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

Pour améliorer les réponses de @Tommy et @Dima, utilisez la catégorie suivante pour rendre UIView en UIImage avec arrière-plan transparent et sans perte de qualité. Travailler sur iOS7. (Ou simplement réutiliser cette méthode dans l'implémentation, en remplaçant self reference avec votre image)

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
24
Glogo

iOSSwift

Utilisation de UIGraphicsImageRenderer moderne

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

Swift 3

La solution Swift 3 (basée sur réponse de Dima ) avec l'extension UIView devrait ressembler à ceci:

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
    }
}
15
Mehdi Hosseinzadeh

Extension Swift 3.0 immédiate prenant en charge la nouvelle API iOS 10.0 et la méthode précédente.

Remarque:

  • vérification de la version iOS
  • Notez l'utilisation de différer pour simplifier le nettoyage du contexte.
  • Appliquera également l'opacité et l'échelle actuelle de la vue.
  • Rien n'est simplement déballé avec !, ce qui pourrait provoquer un crash.

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()
        }
    }
}
6
Leslie Godwin

Swift 2.0:

Utilisation de la méthode d'extension:

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
   }

}

Usage:

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)

 }
5
A.G

Mise en œuvre de Swift 3.0

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

Toutes les Swift 3 réponses n'ont pas fonctionné pour moi, j'ai donc traduit la réponse la plus acceptée:

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

Voici un Swift 4 extension UIView basé sur la réponse de @Dima.

extension UIView {

   func image () -> UIImage?
   {
       UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)

       drawHierarchy(in: bounds, afterScreenUpdates: false)

       let image = UIGraphicsGetImageFromCurrentImageContext()

       UIGraphicsEndImageContext()

       return image
   }
}
1
danfordham

Parfois, la méthode drawRect pose problème, alors ces réponses sont plus appropriées. Vous aussi, vous pouvez le regarder Capture UIImage de UIView bloqué dans la méthode DrawRect

0
Mashhadi
- (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;
}
0
P.J.Radadiya

UIGraphicsImageRenderer est une API relativement nouvelle, introduite dans iOS 10. Vous construisez un UIGraphicsImageRenderer en spécifiant une taille de point . La méthode image utilise un argument de fermeture et renvoie une image bitmap résultant de l'exécution de la fermeture passée. Dans ce cas, le résultat est l'image d'origine réduite pour pouvoir être dessinée dans les limites spécifiées.

https://nshipster.com/image-resizing/

Assurez-vous donc que la taille que vous passez dans UIGraphicsImageRenderer correspond à des points et non à des pixels.

Si vos images sont plus grandes que prévu, vous devez diviser votre taille par le facteur d'échelle.

0
pkamb