27

I'm currently developing a simple photoshop like application on iphone. When I want to flatten my layers, the labels are at the good position but with a bad font size. Here's my code to flatten :

UIGraphicsBeginImageContext(CGSizeMake(widthDocument,widthDocument));

for (UILabel *label in arrayLabel) {
    [label drawTextInRect:label.frame];
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Anybody can help me ?

Jonathan
  • 873
  • 3
  • 9
  • 23

6 Answers6

51

From: pulling an UIImage from a UITextView or UILabel gives white image.

// iOS

- (UIImage *)grabImage {
    // Create a "canvas" (image context) to draw in.
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0);  // high res
    // Make the CALayer to draw in our "canvas".
    [[self layer] renderInContext: UIGraphicsGetCurrentContext()];

    // Fetch an UIImage of our "canvas".
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    // Stop the "canvas" from accepting any input.
    UIGraphicsEndImageContext();

    // Return the image.
    return image;
}

// Swift extension w/ usage. credit @Heberti Almeida in below comments

extension UIImage {
    class func imageWithLabel(label: UILabel) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
        label.layer.renderInContext(UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }
}

The usage:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 30, height: 22))
label.text = bulletString
let image = UIImage.imageWithLabel(label)
Community
  • 1
  • 1
DJPlayer
  • 3,244
  • 2
  • 33
  • 40
  • Yes, I had seen this post but with this solution i can't get à high definition file. But you make me understood where is my problem ! Thank you very much!! – Jonathan Aug 09 '12 at 07:03
  • 7
    @DJPlayer To get a sharp image for retina display, we need to replace the first line in your method by this line: `UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0);` as seen in [this question](http://stackoverflow.com/q/4334233/2471006) and its accepted answer. Perhaps you might want to update your answer including that information. – anneblue Jul 22 '13 at 10:02
  • If you need a transparent image set opaque to `NO` in `UIGraphicsBeginImageContextWithOptions` – Heberti Almeida Nov 05 '15 at 13:39
  • is it possible to render the label to 3 times its actual size without losing sharpness? – Kashif Apr 14 '20 at 19:26
25

I have created a Swift extension to render the UILabel into a UIImage:

extension UIImage {
    class func imageWithLabel(label: UILabel) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
        label.layer.renderInContext(UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }
}

EDIT: Improved Swift 4 version

extension UIImage {
    class func imageWithLabel(_ label: UILabel) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
        defer { UIGraphicsEndImageContext() }
        label.layer.render(in: UIGraphicsGetCurrentContext()!)
        return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
    }
}

The usage is simple:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 30, height: 22))
label.text = bulletString
let image = UIImage.imageWithLabel(label)
Heberti Almeida
  • 1,440
  • 18
  • 27
5

you need to render the label in a context

replace

[label drawTextInRect:label.frame];

with

[label.layer renderInContext:UIGraphicsGetCurrentContext()];

and you will need to create one image per label, so move the for loop to outside the begin and end context calls

wattson12
  • 11,176
  • 2
  • 32
  • 34
2

for Swift, I use this UIView-extension:

// Updated for Swift 3.0
extension UIView {
    var grabbedImage:UIImage? {
        get {
            UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
            layer.render(in: UIGraphicsGetCurrentContext()!)
            let img = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return img
        }
    }
}
AmitaiB
  • 1,656
  • 1
  • 19
  • 19
dr OX
  • 4,671
  • 1
  • 17
  • 9
1

You can use renderInContext on any CALayer. It will draw your view's layer on the context. So that you can change it to an image. Also if you do renderInContext on a view , all its subviews will be drawn onto the context. So instead of iterating through all the labels, while adding the labels, you can add those to a containerView. And just need to do renderInContext on that containerView.

Sj.
  • 1,674
  • 2
  • 15
  • 23
1
extension UIImage {

    class func imageFrom(text: String, width: CGFloat, font: UIFont, textAlignment: NSTextAlignment, lineBreakMode: NSLineBreakMode) -> UIImage? {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
        label.text = text
        label.font = font
        label.textAlignment = textAlignment
        label.lineBreakMode = lineBreakMode

        label.sizeToFit()

        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
        label.layer.render(in: UIGraphicsGetCurrentContext()!)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }

}

Using:

    let iv = UIImageView(image: UIImage.imageFrom(text: "Test For \nYarik", width: 537.0, font: UIFont.systemFont(ofSize: 30), textAlignment: .center, lineBreakMode: .byWordWrapping))
    iv.contentMode = .scaleAspectFit
kolbasek
  • 81
  • 5