रेटिना डिस्प्ले पर गुणवत्ता के नुकसान के बिना UIView को UIImage पर कैसे कब्जा करें


303

मेरा कोड सामान्य उपकरणों के लिए ठीक काम करता है लेकिन रेटिना उपकरणों पर धुंधली छवियां बनाता है।

किसी को भी मेरे मुद्दे का हल पता है?

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

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return img;
}

धुँधली। लगता है मुझे सही पैमाना खो गया ...
डैनियल

मैं भी। उसी मुद्दे पर मिले।
रेनकास्ट

आप परिणाम छवि कहां प्रदर्शित कर रहे हैं? जैसा कि दूसरों ने समझाया है, मेरा मानना ​​है कि आप स्केल 1 वाली छवि पर दृश्य प्रदान कर रहे हैं, जबकि आपके डिवाइस में स्केल 2 या 3 हैं। इसलिए आप कम रिज़ॉल्यूशन के लिए डाउनस्कूल कर रहे हैं। यह गुणवत्ता का नुकसान है लेकिन धुंधला नहीं होना चाहिए। लेकिन जब आप इस छवि को फिर से देख रहे हैं (उसी स्क्रीन पर?) स्केल 2 के साथ एक डिवाइस पर यह निम्न रिज़ॉल्यूशन से उच्चतर में बदल जाएगा, स्क्रीनशॉट में प्रति पिक्सेल 2 पिक्सेल का उपयोग कर। यह अपसंस्कृति इंटरपोलेशन (आईओएस पर डिफ़ॉल्ट), अतिरिक्त पिक्सेल के लिए रंग मानों का औसत उपयोग करती है।
हंस तेरेजे बक्के

जवाबों:


667

के उपयोग से स्विच UIGraphicsBeginImageContextकरने के लिए UIGraphicsBeginImageContextWithOptions(के रूप में दस्तावेज इस पृष्ठ पर )। स्केल (तीसरा तर्क) के लिए पास करें और आपको स्क्रीन के बराबर स्केल फैक्टर वाला एक संदर्भ मिलेगा।

UIGraphicsBeginImageContext1.0 के एक निश्चित पैमाने के कारक का उपयोग करता है, तो आप वास्तव में एक iPhone 4 पर अन्य iPhones के समान छवि प्राप्त कर रहे हैं। मैं शर्त लगा सकता हूँ कि या तो iPhone 4 एक फ़िल्टर लागू कर रहा है जब आप इसे स्पष्ट रूप से मापते हैं या बस आपका मस्तिष्क इसे उठा रहा है, इसके चारों ओर की चीज़ों की तुलना में कम तेज है।

तो मुझे लगता है:

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

और स्विफ्ट 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
}

6
टॉमी उत्तर ठीक है, लेकिन आपको अभी भी चेतावनी #import <QuartzCore/QuartzCore.h>को हटाने के लिए आयात करने की आवश्यकता है renderInContext:
gwdp

2
@WayneLiu आप स्पष्ट रूप से 2.0 के एक पैमाने कारक को निर्दिष्ट कर सकते हैं, लेकिन आपको शायद बिल्कुल iPhone 4 गुणवत्ता वाली छवि नहीं मिलेगी क्योंकि जो भी बिटमैप लोड किए गए थे वे @ 2x संस्करणों के बजाय मानक संस्करण होंगे। मुझे नहीं लगता कि इसका कोई समाधान है क्योंकि सभी को निर्देश देने का कोई तरीका नहीं है जो वर्तमान में एक UIImageको फिर से लोड करने के लिए है, लेकिन उच्च रिज़ॉल्यूशन संस्करण को मजबूर करता है (और संभवत: आप कम रैम के कारण समस्याओं के खिलाफ पूर्व में उपलब्ध होंगे। वैसे भी -retina डिवाइस)।
टॉमी

6
0.0fस्केल पैरामीटर के लिए उपयोग करने के बजाय , क्या यह उपयोग करने के लिए अधिक स्वीकार्य है [[UIScreen mainScreen] scale], यह भी काम करता है।
एडम कार्टर

20
@ अदम कार्टर scale: The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.यह स्पष्ट रूप से प्रलेखित है, इसलिए मेरी राय में 0.0f सरल और बेहतर है।
cccrack

5
यह उत्तर iOS 7 के लिए पुराना है। ऐसा करने के लिए नई "सर्वश्रेष्ठ" विधि के लिए मेरा उत्तर देखें।
दिमा

224

वर्तमान में स्वीकृत उत्तर अब पुराना है, कम से कम यदि आप iOS 7 का समर्थन कर रहे हैं।

यदि आप केवल 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;
}

स्विफ्ट 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()
}

इस लेख के अनुसार , आप देख सकते हैं कि नई iOS7 विधि drawViewHierarchyInRect:afterScreenUpdates:की तुलना में कई गुना तेज है renderInContext:बेंचमार्क


5
इसमें कोई शक नहीं है, यह drawViewHierarchyInRect:afterScreenUpdates:बहुत तेज है। मैंने सिर्फ इंस्ट्रूमेंट्स में टाइम प्रोफाइलर चलाया। मेरी छवि पीढ़ी 138ms से 27ms तक चली गई।
थॉमसकैल

9
जब मैं Google मानचित्र मार्करों के लिए कस्टम आइकन बनाने का प्रयास कर रहा था तो किसी कारण से यह मेरे लिए काम नहीं कर रहा था; मुझे जो कुछ मिला वह काला आयत था :(
जेकप

6
@CarlosP क्या आपने सेटिंग afterScreenUpdates:करने की कोशिश की है YES?
दीमा

7
YSES के बाद सेट करेंस्क्रीनअप मेरे लिए काला आयत का मुद्दा तय करता है
hyouuu

4
मुझे काले चित्र भी प्राप्त हो रहे थे। यह पता चला कि अगर मैं कोड को कॉल करता हूं viewDidLoadया viewWillAppear:चित्र काले हैं; मुझे इसमें करना था viewDidAppear:। इसलिए मैं भी अंत में लौट आया renderInContext:
मणिकासेर

32

मैंने @ समाधान के आधार पर एक स्विफ्ट एक्सटेंशन बनाया है:

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

संपादित करें: स्विफ्ट 4 बेहतर संस्करण

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

उपयोग:

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

2
विचार के लिए धन्यवाद! एक तरफ के रूप में, आप संदर्भ शुरू करने के तुरंत बाद {UIGraphicsEndImageContext ()} को भी स्थगित कर सकते हैं और स्थानीय वैरिएबल img को शुरू करने से बच सकते हैं;)
डेनिस एल

अच्छी तरह से विस्तृत विवरण और उपयोग के लिए बहुत बहुत धन्यवाद!
ज़ीउस

यह एक classफ़ंक्शन क्यों है जो एक दृश्य लेता है?
रुडोल्फ एडमकोविच

यह एक staticफ़ंक्शन की तरह काम करता है लेकिन जैसा कि आपको ओवरराइड करने की कोई आवश्यकता नहीं है, आप इसे staticबदले में इसे एक फंक में बदल सकते हैं class
हेबरती अल्मीडा

25

आईओएस तीव्र

आधुनिक 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
    }
}

@ masaldana2 शायद अभी तक नहीं है, लेकिन जैसे ही वे सामान को हटाना शुरू करते हैं या हार्डवेयर त्वरित रेंडरिंग जोड़ते हैं या आपको बेहतर रूप से दिग्गजों के कंधों (AKA पिछले Apple APIs का उपयोग करते हुए) में सुधार करते हैं
Juan Boero

क्या किसी को पता है कि प्रदर्शन की तुलना rendererपुराने से क्या है drawHierarchy..?
Vive

24

@ टॉमी और @ डीमा द्वारा जवाबों को बेहतर बनाने के लिए, पारदर्शी पृष्ठभूमि के साथ और गुणवत्ता की हानि के बिना UIImage में UIView को प्रस्तुत करने के लिए निम्न श्रेणी का उपयोग करें । IOS7 पर काम कर रहा है। (या कार्यान्वयन में उस विधि का पुन: उपयोग करें, selfअपनी छवि के साथ संदर्भ की जगह )

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

1
अगर मैं afterScreenUpdates के साथ drawViewHierarchyInRect का उपयोग करता हूं: तो हां मेरी छवि पहलू अनुपात बदल गया है और छवि विकृत हो जाती है।
मिलाप

क्या यह तब होता है जब आपकी uiview सामग्री बदली जाती है? यदि हाँ, तो इस UIImage को फिर से जनरेट करने का प्रयास करें। क्षमा करें, मैं स्वयं इसका परीक्षण नहीं कर सकता क्योंकि मैं फ़ोन पर हूँ
गॉल्गो

मुझे भी यही समस्या है और ऐसा लगता है कि किसी ने इस पर ध्यान नहीं दिया, क्या आपने इस समस्या का कोई हल खोजा? @confile?
रेजा।अब

यह वही है जो मुझे चाहिए लेकिन यह काम नहीं किया। मैंने बनाने @interfaceऔर @implementationदोनों को हेडर में (RenderViewToImage)जोड़ने की कोशिश की #import "UIView+RenderViewToImage.h"है ViewController.hतो क्या यह सही उपयोग है? जैसे UIImageView *test = [[UIImageView alloc] initWithImage:[self imageByRenderingView]]; [self addSubview:test];?
ग्रेग

और इस पद्धति में ViewController.mमैंने उस दृश्य को प्रस्तुत करने की कोशिश की- (UIView *)testView { UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)]; v1.backgroundColor = [UIColor redColor]; UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 150, 150)]; v2.backgroundColor = [UIColor blueColor]; [v2 addSubview:v1]; return v2; }
ग्रेग

15

स्विफ्ट 3

स्विफ्ट 3 समाधान (के आधार पर दीमा का जवाब ) UIView विस्तार के साथ इस तरह होना चाहिए:

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

6

ड्रॉप-इन स्विफ्ट 3.0 एक्सटेंशन जो नए iOS 10.0 API और पिछले विधि का समर्थन करता है।

ध्यान दें:

  • iOS संस्करण की जाँच करें
  • संदर्भ सफाई को आसान बनाने के लिए defer के उपयोग पर ध्यान दें।
  • दृश्य की अस्पष्टता और वर्तमान पैमाना भी लागू करेगा।
  • कुछ भी नहीं है !जो दुर्घटना का कारण बन सकता है का उपयोग नहीं करता है ।

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

स्विफ्ट 2.0:

एक्सटेंशन विधि का उपयोग करना:

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
   }

}

उपयोग:

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)

 }

3

स्विफ्ट 3.0 कार्यान्वयन

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

3

सभी स्विफ्ट 3 उत्तरों ने मेरे लिए काम नहीं किया इसलिए मैंने सबसे स्वीकृत उत्तर का अनुवाद किया है:

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

2

यहां @Dima के उत्तर के आधार पर एक स्विफ्ट 4 UIView एक्सटेंशन है

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

2

स्विफ्ट 5.1 के लिए आप इस एक्सटेंशन का उपयोग कर सकते हैं:

extension UIView {

    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)

        return renderer.image {
            rendererContext in

            layer.render(in: rendererContext.cgContext)
        }
    }
}

1

UIGraphicsImageRendereriOS 10 में पेश किया गया एक अपेक्षाकृत नया एपीआई है, आप एक बिंदु आकार निर्दिष्ट करके UIGraphicsImageRenderer का निर्माण करते हैं । छवि विधि एक क्लोजर तर्क लेती है और एक बिटमैप लौटाती है जिसके परिणामस्वरूप पारित क्लोजर निष्पादित होता है। इस मामले में, परिणाम मूल छवि को निर्दिष्ट सीमा के भीतर खींचने के लिए नीचे है।

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

इसलिए सुनिश्चित करें कि आप जिस आकार में गुजर रहे हैं UIGraphicsImageRendererवह अंक है, न कि पिक्सेल।

यदि आपकी छवियां आपकी अपेक्षा से बड़ी हैं, तो आपको अपने आकार को स्केल फैक्टर द्वारा विभाजित करने की आवश्यकता है।


मैं उत्सुक हूं, क्या आप कृपया मेरी मदद कर सकते हैं? stackoverflow.com/questions/61247577/… मैं UIGraphicsImageRenderer का उपयोग कर रहा हूं, समस्या यह है कि मैं चाहता हूं कि निर्यात की गई छवि डिवाइस पर वास्तविक दृश्य से बड़े आकार की हो। लेकिन वांछित आकार के साक्षात्कार (लेबल) काफी तेज नहीं हैं - इसलिए गुणवत्ता का नुकसान होता है।
ओन्दोज कोरोल


0
- (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;
}

-2

इस विधि में सिर्फ एक व्यू ऑब्जेक्ट पास होता है और यह एक UIImage ऑब्जेक्ट लौटाएगा।

-(UIImage*)getUIImageFromView:(UIView*)yourView
{
 UIGraphicsBeginImageContext(yourView.bounds.size);
 [yourView.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 return image;
}

-6

इस विधि को UIView श्रेणी में जोड़ें

- (UIImage*) capture {
    UIGraphicsBeginImageContext(self.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}

2
हिया वहाँ, जबकि यह अच्छी तरह से सवाल का जवाब दे सकता है, कृपया ध्यान रखें कि अन्य उपयोगकर्ता आपके जैसे ज्ञानी नहीं हो सकते हैं। आप इस कोड के काम करने के बारे में थोड़ा स्पष्टीकरण क्यों नहीं जोड़ते हैं? धन्यवाद!
वोगेल 612
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.