1

I want to stretch the text inside a UILabel so that if fits exactly into the label (both width and height). I don't want to resize the UILabel in any way.

So far i used this: How to render stretched text in iOS? , but the text doesn't stretch 100% ( sometimes it exceeds the boundaries, and sometimes it leaves spacing on the margins ).

Is there another (preferably easier) way to do this?

This is what i was talking about: https://i.stack.imgur.com/wHoHG.png . I get spacing on the left and the text exceeds boundaries on the right and also on the bottom edge.

This is the custom label class:

#import "CustomUILabel.h"

@implementation CustomUILabel

- (id)initWithFrame:(CGRect)frame text:(NSString*)text
{
    self = [super initWithFrame:frame];
    if (self) {
        self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
        self.text = text;
    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.edgeInsets)];
}

- (void)drawRect:(CGRect)rect
{
    [self drawScaledString:self.text];
}

- (void)drawScaledString:(NSString *)string
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    NSAttributedString *attrString = [self generateAttributedString:string];

    CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attrString, CFRangeMake(0, string.length),
                                   kCTForegroundColorAttributeName, self.textColor.CGColor);

    CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) attrString);

    // CTLineGetTypographicBounds doesn't give correct values,
    // using GetImageBounds instead
    CGRect imageBounds = CTLineGetImageBounds(line, context);
    CGFloat width = imageBounds.size.width;
    CGFloat height = imageBounds.size.height;

    CGFloat padding = 0;

    width += padding;
    height += padding;

    float sx = self.bounds.size.width / width;
    float sy = self.bounds.size.height / height;

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    CGContextTranslateCTM(context, 1, self.bounds.size.height);
    CGContextScaleCTM(context, 1, -1);
    CGContextScaleCTM(context, sx, sy);

    CGContextSetTextPosition(context, -imageBounds.origin.x + padding/2, -imageBounds.origin.y + padding/2);

    CTLineDraw(line, context);
    CFRelease(line);
}

- (NSAttributedString *)generateAttributedString:(NSString *)string
{

    CTFontRef helv = CTFontCreateWithName(CFSTR("Helvetica-Bold"),20, NULL);
    CGColorRef color = [UIColor blackColor].CGColor;

    NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
                                    (__bridge id)helv, (NSString *)kCTFontAttributeName,
                                    color, (NSString *)kCTForegroundColorAttributeName,
                                    nil];

    NSAttributedString *attrString = [[NSMutableAttributedString alloc]
                                       initWithString:string
                                       attributes:attributesDict];

    return attrString;
}

@end

And this is how i use it (I've added the label from storyboards):

@property (weak, nonatomic) IBOutlet CustomUILabel *label;

...

self.label.backgroundColor = [UIColor redColor];
self.label.text = @"OOOOO";
Community
  • 1
  • 1
meee
  • 23
  • 4
  • do you mean you want to really stretch the text or that you want to fit the font size depending on the label size? – Vik Jan 06 '14 at 11:48
  • The other question is sound in theory so it suggests you have some calculation errors. Show your code and screenshots of the issues you see for specified inputs. – Wain Jan 06 '14 at 11:49
  • I really want to stretch the text. – meee Jan 06 '14 at 11:52
  • I've added a screenshot of the result and the code for the label. I subclass the UILabel with CustomUILabel. – meee Jan 06 '14 at 12:06

1 Answers1

0

What you're asking boils down to calculating the exact bounding box of rendered text. When you have this box you can adjust the CTM to make the text fill the desired area.

Yet: There does not seem to be an easy way to do this. A lot of issues contribute to this problem:

  1. Font metrics are a pretty complex topic. A rendered character (a glyph) has several bounding boxes depending on the intention.
  2. In fonts, glyphs are often represented using bezier curves which makes calculating an exact bounding box difficult.
  3. Attributes might influence graphical appearance in unforeseeable ways. AppKit/UIKit for example know a shadow attribute that can extend the area in which the font renders pixels.

There are more issues but the ones I listed might be enough to show that the task of exactly filling a box with a rendered text is not so easy.

Maybe there's another way of doing what you have in mind.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • Thanks. I hoped that maybe there is a solution, but i'm beginning to believe that there is no solution :) ( at least not one that i can figure out ) . I'll try to find another solution. – meee Jan 06 '14 at 12:37