Comment capturer UIView vers UIImage sans perte de qualité sur l'affichage retina

mon code fonctionne bien pour les appareils normaux mais crée des images floues sur les appareils rétiniens.

quelqu'un connais une solution pour mon problème?

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

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}
275
demandé sur Ricardo Sanchez-Saez 2010-12-02 14:02:43

15 réponses

passer de l'utilisation de UIGraphicsBeginImageContext à UIGraphicsBeginImageContextWithOptions (comme l'a documenté sur cette page ). Passer 0,0 échelle (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, donc vous obtenez exactement la même image sur un iPhone 4 que sur les autres iPhones. Je parie que soit l'iPhone 4 applique un filtre quand vous l'augmentez implicitement ou juste votre le cerveau se rend compte qu'il est moins tranchant 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
}
634
répondu Tommy 2018-03-10 11:08:16

la réponse acceptée est maintenant périmée, du moins si vous appuyez iOS 7.

voici ce que vous devez 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)
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
        let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return snapshotImage
    }

comme dans cet article , vous pouvez voir que la nouvelle méthode iOS7 drawViewHierarchyInRect:afterScreenUpdates: est beaucoup plus rapide que renderInContext: . benchmark

191
répondu Dima 2018-09-04 17:06:16

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

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

Utilisation:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))  
let image = UIImage.imageWithView(view)
30
répondu Heberti Almeida 2018-03-08 16:45:51

pour améliorer les réponses de @Tommy et @Dima, utilisez la catégorie suivante pour rendre uivi en UIImage avec fond transparent et sans perte de qualité. Je travaille sur iOS7. (Ou tout simplement réutiliser cette méthode dans la mise en œuvre, en remplaçant self référence avec votre image)

Uivi+RenderViewToImage.h

#import <UIKit/UIKit.h>

@interface UIView (RenderToImage)

- (UIImage *)imageByRenderingView;

@end

Uivi+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
répondu Glogo 2015-06-21 19:12:31

iOS Swift

modernes UIGraphicsImageRenderer

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
    }
}
15
répondu Alexander Belyavskiy 2016-12-03 08:17:33

Swift 3

la solution Swift 3 (basée sur Dima's answer ) avec UIView extension devrait être comme 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
répondu Mehdi Hosseinzadeh 2017-05-23 12:02:56

Swift 2.0:

utilisant la méthode de l'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
   }

}

Utilisation:

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
répondu A.G 2016-04-30 05:34:22

Drop-in Swift 3.0 extension qui supporte la nouvelle API iOS 10.0 et la méthode précédente.

Note:

  • version iOS cochez la case
  • noter l'utilisation de defer pour simplifier le nettoyage du contexte.
  • appliquera également l'opacité et l'échelle actuelle de la vue.
  • Rien n'est juste déballé à l'aide de ! , ce qui pourrait causer un accident.

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()
        }
    }
}
5
répondu Leslie Godwin 2017-05-26 07:16:35

Swift 3.0 mise en œuvre

extension UIView {
    func getSnapshotImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
        drawHierarchy(in: bounds, afterScreenUpdates: false)
        let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return snapshotImage
    }
}
3
répondu Mihael Isaev 2016-10-07 21:38:48

toutes les réponses Swift 3 n'ont pas fonctionné pour moi donc j'ai 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!
    }
}
3
répondu Marco Antonio Uzcategui Pescoz 2017-05-09 12:06:24

parfois la méthode drawRect rend problème donc j'ai obtenu ces réponses plus appropriées. Vous aussi, vous pourriez y jeter un oeil. Capture UIImage de UIView coincé dans la méthode DrawRect

0
répondu Mashhadi 2017-05-23 12:26:27
- (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
répondu P.J.Radadiya 2016-03-01 06:05:46

voici un Swift 4 UIView extension 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
   }
}
0
répondu danfordham 2018-06-06 20:02:12

dans cette méthode il suffit de passer un objet view et il retournera un objet UIImage.

-(UIImage*)getUIImageFromView:(UIView*)yourView
{
 UIGraphicsBeginImageContext(yourView.bounds.size);
 [yourView.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 return image;
}
-2
répondu Atanu Mondal 2016-04-12 12:29:51

ajouter cette méthode à la catégorie uivi

- (UIImage*) capture {
    UIGraphicsBeginImageContext(self.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}
-7
répondu Shafraz Buhary 2013-01-30 06:44:01