How to render UILabel at certain point in UIImage?

86 views Asked by At

I'm using code found here to create an image with text that is scaled to available size:

CGFloat size = 100.0;
CGRect drawRect = CGRectMake(10, 10, 80, 25);

UILabel *myLabel = [[UILabel alloc] initWithFrame:drawRect];
myLabel.font = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:16];
myLabel.text = "Hello text!";
myLabel.minimumScaleFactor = 0.5;
myLabel.adjustsFontSizeToFitWidth = YES;
myLabel.textAlignment = NSTextAlignmentCenter;
myLabel.backgroundColor = [UIColor clearColor];

UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, 0);
[[myLabel layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[screenshot drawInRect:drawRect];

return screenshot;

This creates a 100x100 image with the rendered label in the top-left corner: (0, 0). How do I get the text at the desired point (10, 10)?

To clarify: I want the label to be the size I specify, be centred horizontally, and its text to scale according to the available size.

Also, what is the purpose of [screenshot drawInRect:drawRect] because I seem to get the same result without it?

3

There are 3 answers

2
DonMag On BEST ANSWER

To answer your specific questions...

First, as to "what is the purpose of [screenshot drawInRect:drawRect]" ...

It has no purpose there.

Presumably, the post you got that from intended to say something like: "Now you have a 100 x 100 UIImage "screenshot" which you can draw into some other rect in some other graphics context."


Second...

The label is not drawing at (10, 10) because you are rendering the label's layer -- which has an origin of (0, 0).

If you want it rendered at (10, 10) you can modify the layer's bounds before drawing it:

myLabel.backgroundColor = [UIColor clearColor];

// change the label's layer bounds
//  to the drawRect rect
myLabel.layer.bounds = drawRect;

UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, 0);

So, if we run your code (no idea why you have two let statements):

CGFloat size = 100.0;
CGRect drawRect = CGRectMake(10, 10, 80, 25);

UILabel *myLabel = [[UILabel alloc] initWithFrame:drawRect];
myLabel.font = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:16];
myLabel.text = "Hello text!";
myLabel.minimumScaleFactor = 0.5;
myLabel.adjustsFontSizeToFitWidth = YES;
myLabel.textAlignment = NSTextAlignmentCenter;
myLabel.backgroundColor = [UIColor clearColor];

// change the label's layer bounds
//  to match the drawRect
myLabel.layer.bounds = drawRect;

UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, 0);
[[myLabel layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

// do something with screenshot, such as
someImageView.image = screenshot;

You will now have a 100 x 100 point (not pixel) UIImage named "screenshot" with the label frame at (10, 10) (again, 10-points not pixels).

This is the result... note that the code you posted specifies a CGContextRef at the device’s main screen scale factor. I'm running this on an iPhone 14 Pro which has a @3x factor, so the output is a 300x300 pixel image, with the label frame at (30, 30) pixels:

enter image description here

Since it has a clear background, that's how it looks in a paint app.

If we modify the code just a little to add color to fill the image rect and the label frame, it looks like this:

enter image description here


As a side-note, you might want to look into the more "modern" UIGraphicsImageRenderer

0
son On

It's quite simple, just wrap your label in a view then capture the view instead of the label.

CGRect drawRect = CGRectMake(0, 0, 90, 35);
UIView *container = [[UIView alloc] initWithFrame: drawRect]
...
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 80, 25)];
[container addSubview:myLabel];
...
[[container layer] renderInContext:UIGraphicsGetCurrentContext()];

Before

enter image description here

After

enter image description here

0
danh On

Inset the rectangle used to initialize the label's frame...

// ...
let drawRect = CGRectMake(10, 10, 80, 25);

// Inset the drawRect with UIEdgeInsetsInsetRect()
// I've set the (... bottom, left) insets to (... -20, -20) in case
// you wish to center the label. If not, set those to (... 0, 0)
const UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, -20, -20);
const CGRect insetRect = UIEdgeInsetsInsetRect(drawRect, insets);

// changed drawRect to insetRect
UILabel *myLabel = [[UILabel alloc] initWithFrame: insetRect];
// ...

Disclaimer: it has been years since I used Objective-C.

(Regarding what the drawInRect: is for, that's how you might choose to use the image after creating it. You might choose to draw it. If you don't need to, or are drawing it elsewhere later, you can remove that line).