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;
}
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
}
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:
.
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)
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
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
}
}
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
}
}
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)
}
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()
}
}
}
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
}
}
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!
}
}
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
- (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;
}
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
}
}
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;
}
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;
}