Calculating UILabel Text Size

IosObjective CUilabelFrame

Ios Problem Overview


I am drawing UILabels programmatically. They get their sizes from a database. So I cannot just use sizeToFit. I have already implemented a function that redraws UILabels with a passed ratio. So all I need to find is the text in UILabel from my view that would require the maximum ratio to redraw UILabels. So finally I need to do something like this:

    double ratio = 1.00;
    for (UILabel* labels in sec.subviews) {

        float widthLabel = labels.frame.size.width;
        float heightLabel = labels.frame.size.height;
        float heightText = //get the text height here
        float widthText = //get the text width here
        if (widthLabel < widthText) {
            ratio = MAX(widthText/widthLabel,ratio);
        }
        if (heightLabel < heightText) {
            ratio = MAX(heightText/heightLabel, ratio);
        }
    }
    //redraw UILabels with the given ratio here

So how can I get the height and width size of a text, as some of my text do not fit into the label I cannot simply use label bounds? I am using Xcode 5 and iOS 7.

Ios Solutions


Solution 1 - Ios

All of the [NSString sizeWithFont...] methods are deprecated in iOS 7. Use this instead.

CGRect labelRect = [text
                    boundingRectWithSize:labelSize
                    options:NSStringDrawingUsesLineFragmentOrigin
                    attributes:@{
                     NSFontAttributeName : [UIFont systemFontOfSize:14]
                    }
                    context:nil];

Also see https://developer.apple.com/documentation/foundation/nsstring/1619914-sizewithfont.

UPDATE - example of boundingRectWithSize output

Per your comment I did a simple test. The code and output is below.

// code to generate a bounding rect for text at various font sizes
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
for (NSNumber *n in @[@(12.0f), @(14.0f), @(18.0f)]) {
    CGFloat fontSize = [n floatValue];
    CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
                                  options:NSStringDrawingUsesLineFragmentOrigin
                               attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                                  context:nil];
    NSLog(@"fontSize = %f\tbounds = (%f x %f)",
          fontSize,
          r.size.width,
          r.size.height);
}

this produces the following output (note that the bounds change as expected as the font size gets larger):

fontSize = 12.000000    bounds = (181.152008 x 28.632000)
fontSize = 14.000000    bounds = (182.251999 x 50.105999)
fontSize = 18.000000    bounds = (194.039993 x 64.421997)

Solution 2 - Ios

Length gets the number of characters. If you want to get the width of the text:

Objective-C

CGSize textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[label font]}];

Swift 4

let size = label.text?.size(withAttributes: [.font: label.font]) ?? .zero

This gets you the size. And you can compare the textSize.width of each label.

Solution 3 - Ios

Another simple way to do this that I haven't seen mentioned yet:

CGSize textSize = [label intrinsicContentSize];

(This only works correctly after you have set the label's text and font, of course.)

Solution 4 - Ios

Here is a swift variant.

let font = UIFont(name: "HelveticaNeue", size: 25)!
let text = "This is some really long text just to test how it works for calculating heights in swift of string sizes. What if I add a couple lines of text?"

let textString = text as NSString

let textAttributes = [NSFontAttributeName: font]

textString.boundingRectWithSize(CGSizeMake(320, 2000), options: .UsesLineFragmentOrigin, attributes: textAttributes, context: nil)

Solution 5 - Ios

Little advice guys, if like me you're using, boundingRectWithSize with [UIFont systemFontOFSize:14]

If your string is two lines long, the returned rect height is something like 33,4 points.

Don't make the mistake, like me, to cast it into an int, because 33,4 becomes 33, and 33 points height label pass from two to one line!

Solution 6 - Ios

The problem with

CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
                              options:NSStringDrawingUsesLineFragmentOrigin
                           attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                              context:nil];

is boundingRectWithSize which determines the maximum value that CGRect can have.

My solution for this problem is to check if it exceeds, if not then text can fit into the label. I did it by using loops.

NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
bool sizeFound = false;
while (!sizeFound) {
    NSLog(@"Begin loop");
    CGFloat fontSize = 14;
    CGFloat previousSize = 0.0;
    CGFloat currSize = 0.0;
    for (float fSize = fontSize; fSize < fontSize+6; fSize++) {
        CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
                                      options:NSStringDrawingUsesLineFragmentOrigin
                                   attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fSize]}
                                      context:nil];
        currSize =r.size.width*r.size.height;
        if (previousSize >= currSize) {
            width = width*11/10;
            height = height*11/10;
            fSize = fontSize+10;
        }
        else {
            previousSize = currSize;
        }
        NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
              fSize,
              r.size.width,
              r.size.height,r.size.width*r.size.height);
    }
    if (previousSize == currSize) {
        sizeFound = true;
    }
    
}
NSLog(@"Size found with width %f and height %f", width, height);

After each iteration the size of height and width increments 10% of its value.

The reason why I picked 6 is because I did not want the label to be too squishy.

For a solution that does not use loops:

NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;

CGFloat currentFontSize = 12;
CGRect r1 = [text boundingRectWithSize:CGSizeMake(width, height)
                              options:NSStringDrawingUsesLineFragmentOrigin
                           attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
                              context:nil];

CGRect r2 = [text boundingRectWithSize:CGSizeMake(width, height)
                               options:NSStringDrawingUsesFontLeading
                            attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
                               context:nil];

CGFloat firstVal =r1.size.width*r1.size.height;
CGFloat secondVal =r2.size.width*r2.size.height;

NSLog(@"First val %f and second val is %f", firstVal, secondVal);

if (secondVal > firstVal) {
    float initRat = secondVal/firstVal;
    
    float ratioToBeMult = sqrtf(initRat);
    
    width *= ratioToBeMult;
    height *= ratioToBeMult;
}

NSLog(@"Final width %f and height %f", width, height);

//for verifying
for (NSNumber *n in @[@(12.0f), @(14.0f), @(17.0f)]) {
    CGFloat fontSize = [n floatValue];
    CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
                                  options:NSStringDrawingUsesLineFragmentOrigin
                               attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
                                  context:nil];
    NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
          fontSize,
          r.size.width,
          r.size.height,r.size.width*r.size.height);
    firstVal =r.size.width*r.size.height;
}

Where the last loop is proof that larger font can give a higher size result.

Solution 7 - Ios

A solution that works with multiline labels (Swift 4), to calculate the height from a fixed width:

let label = UILabel(frame: .zero)
label.numberOfLines = 0 // multiline
label.font = UIFont.systemFont(ofSize: UIFont.labelFontSize) // your font
label.preferredMaxLayoutWidth = width // max width
label.text = "This is a sample text.\nWith a second line!" // the text to display in the label

let height = label.intrinsicContentSize.height

Solution 8 - Ios

By using this line of code we can get the size of text on the label.

let str = "Sample text"
let size = str.sizeWithAttributes([NSFontAttributeName:UIFont.systemFontOfSize(17.0)])

So, we can use the both width and height.

Solution 9 - Ios

msgStr string get size :

let msgStr:NSString = Data["msg"]! as NSString
let messageSize = msgStr.boundingRect(with: CGSize(width: ChatTable.frame.width-116, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont(name: "Montserrat-Light", size: 14)!], context: nil).size

Solution 10 - Ios

Swift 3.0

func getLabelHeight() -> CGFloat {
    let font = UIFont(name: "OpenSans", size: 15)!
    let textString = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." as NSString
    
    let textAttributes = [NSFontAttributeName: font]
    
    let rect = textString.boundingRect(with: CGSize(width: 320, height: 2000), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
    return rect.size.height
}

Solution 11 - Ios

It's a really ugly mess given that if you set UILabel font after you have set it with attributedString it clobbers the font info in attributed text and you have to compute based on text+font attributes

Something to the tune of

    CGFloat promptLabelMaxWidth = self.promptLabel.frame.size.width;
    NSAttributedString *attributedText = self.promptLabel.attributedText;
    assert(attributedText);
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){promptLabelMaxWidth, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];
    NSString *text = self.promptLabel.text;
    UIFont *font = self.promptLabel.font;
    if (font) {
        CGRect r = [text boundingRectWithSize: CGSizeMake(promptLabelMaxWidth, CGFLOAT_MAX)
                                          options:NSStringDrawingUsesLineFragmentOrigin
                                       attributes:@{NSFontAttributeName: font}
                                          context:nil];
        if (r.size.height > rect.size.height) {
            rect = r;
        }
    }

Solution 12 - Ios

Swift 5:

 func getTextBounds(_ label : UILabel) -> CGRect {
    if label.text != nil && label.font != nil {
        return label.text!.boundingRect(
                      with: CGSize(width: 450, height: 44),
                      options: [],
                      attributes: [NSAttributedString.Key.font : label.font!],
                      context: nil)
    }
    return CGRect.null
}


 func getTextBounds(_ textField : UITextField) -> CGRect {
    if textField.text != nil && textField.font != nil {
        return textField.text!.boundingRect(
                      with: CGSize(width: 450, height: 44),
                      options: [],
                      attributes: [NSAttributedString.Key.font : textField.font!],
                      context: nil)
    }
    return CGRect.null
}

Or, as extensions:

extension UILabel {
    func textBounds() -> CGRect {
        if self.text != nil && self.font != nil {
            return self.text!.boundingRect(
                          with: CGSize(width: 450, height: 44),
                          options: [],
                          attributes: [NSAttributedString.Key.font : self.font!],
                          context: nil)
        }
        return CGRect.null
    }
}

extension UITextField {
    func textBounds() -> CGRect {
        if self.text != nil && self.font != nil {
            return self.text!.boundingRect(
                          with: CGSize(width: 450, height: 44),
                          options: [],
                          attributes: [NSAttributedString.Key.font : self.font!],
                          context: nil)
        }
        return CGRect.null
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSarp KayaView Question on Stackoverflow
Solution 1 - IosXJonesView Answer on Stackoverflow
Solution 2 - IosH. Serdar ÇınarView Answer on Stackoverflow
Solution 3 - IospoffView Answer on Stackoverflow
Solution 4 - IosCollinView Answer on Stackoverflow
Solution 5 - IosMartinView Answer on Stackoverflow
Solution 6 - IosSarp KayaView Answer on Stackoverflow
Solution 7 - IoschrisbenView Answer on Stackoverflow
Solution 8 - IosRamakrishnaView Answer on Stackoverflow
Solution 9 - IosJayesh MiruliyaView Answer on Stackoverflow
Solution 10 - IosAbhishek JainView Answer on Stackoverflow
Solution 11 - IosAnton TropashkoView Answer on Stackoverflow
Solution 12 - IosclearlightView Answer on Stackoverflow