Organizational Research By

Surprising Reserch Topic

boundingrectwithsize for nsattributedstring returning wrong size

boundingrectwithsize for nsattributedstring returning wrong size  using -'iphone,ios,ipad'

I am trying to get the rect for an attributed string, but the boundingRectWithSize call is not respecting the size I pass in and is returning a rect with a single line height as opposed to a large height (it is a long string). I have experimented by passing in a very large value for the height and also 0 as in the code below, but the rect returned is always the same.

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)

Is this broken, or do I need to do something else to have it returned a rect for wrapped text?

asked Oct 6, 2015 by tushar2k6
0 votes

Related Hot Questions

15 Answers

0 votes

Looks like you weren't providing the correct options. For wrapping labels, provide at least:

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
answered Oct 6, 2015 by santosh soni
0 votes

That method seems buggy in numerous ways. For one, as you note, it doesn't respect width constraints. For another, I've seen it crash because it seems to assume that all attributes are of NSObject type (for example, it tried to pass _isDefaultFace to a CTFontRef). It will also crash sometimes when a string drawing context is provided because it tries to add a nil-valued attribute to a mutable attributed string behind the scenes.

I would encourage you to avoid this method entirely. You can use Core Text directly to estimate the string size, if you can handle the overhead of creating a framesetter for each string you need to draw. It doesn't precisely honor width constraints either, but it seems to get within a few pixels, in my experience.

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attrString);
CGSize targetSize = CGSizeMake(320, CGFLOAT_MAX);
CGSize fitSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [attrString length]), NULL, targetSize, NULL);
answered Oct 6, 2015 by r3tt
0 votes

Ed McManus has certainly provided a key to getting this to work. I found a case that does not work

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     font, NSFontAttributeName,
                                     color, NSForegroundColorAttributeName,

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];

[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];

CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

rect will not have the correct height. Notice that anotherString (which is appended to string) was initialized without an attribute dictionary. This is a legitimate initializer for anotherString but boundingRectWithSize: does not give an accurate size in this case.

answered Oct 6, 2015 by rajnipancholi
0 votes

For some reason, boundingRectWithSize always returns wrong size. I figured out a solution. There is a method for UItextView -sizeThatFits which returns the proper size for the text set. So instead of using boundingRectWithSize, create an UITextView, with a random frame, and call its sizeThatFits with the respective width and CGFLOAT_MAX height. It returns the size that will have the proper height.

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];   
   CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];

If you are calculating the size in a while loop, do no forget to add that in an autorelease pool, as there will be n number of UITextView created, the run time memory of the app will increase if we do not use autoreleasepool.

answered Oct 6, 2015 by ashish singh
0 votes

In case you'd like to get bounding box by truncating the tail, this question can help you out.

CGFloat maxTitleWidth = 200;

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
                             NSParagraphStyleAttributeName: paragraph};

CGRect box = [self.textLabel.text
              boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
              attributes:attributes context:nil];
answered Oct 6, 2015 by vickeykumar66
0 votes

My final decision after long investigation - boundingRectWithSize function returns correct size for uninterrupted sequence of characters only! In case string contains spaces or something else (called by Apple "Some of the glyphs" ) - it is impossible to get actual size of rect needed to display text! I have replaced spaces in my strings by letters and immediately got correct result.

Apple says here:

"This method returns the actual bounds of the glyphs in the string. Some of the glyphs (spaces, for example) are allowed to overlap the layout constraints specified by the size passed in, so in some cases the width value of the size component of the returned CGRect can exceed the width value of the size parameter."

So it is necessary to find some another way to calculate actual rect...

After long investigation process solution finally found!!! I am not sure it will work good for all cases related to UITextView, but main and important thing was detected!

boundingRectWithSize function as well as CTFramesetterSuggestFrameSizeWithConstraints (and many other methods) will calculate size and text portion correct when correct rectangle used. For example - UITextView has textView.bounds.size.width - and this value not actual rectangle used by system when text drawing on UITextView.

I found very interesting parameter and performed simple calculation in code:

CGFloat padding = textView.textContainer.lineFragmentPadding;

CGFloat actualPageWidth = textView.bounds.size.width - padding * 2;

And magic works - all my texts calculated correct now! Enjoy!

answered Oct 6, 2015 by jekbishnoi
0 votes

I have had the same problem with not getting an accurate size using these techniques and I've changed my approach to make it work.

I have a long attributed string which I've been trying to fit into a scroll view so that it shows properly without being truncated. What I did to make the text work reliably was to not set the height at all as a constraint and instead allowed the intrinsic size to take over. Now the text is displayed correctly without being truncated and I do not have to calculate the height.

I suppose if I did need to get the height reliably I would create a view which is hidden and these constraints and get the height of the frame once the constraints are applied.

answered Oct 6, 2015 by vibhorsingh
0 votes

@warrenm Sorry to say that framesetter method didn't work for me.

I got this.This function can help us to determine the frame size needed for a string range of an NSAttributedString in iphone/Ipad SDK for a given Width :

It can be used for a dynamic height of UITableView Cells

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CGFloat width = YOUR_FIXED_WIDTH;

    CFIndex offset = 0, length;
    CGFloat y = 0;
    do {
        length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
        CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));

        CGFloat ascent, descent, leading;
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading);


        offset += length;
        y += ascent + descent + leading;
    } while (offset < [attributedString length]);


    return CGSizeMake(width, ceil(y));

Thanks to HADDAD ISSA >>>

answered Oct 6, 2015 by gauravsinghal83
0 votes

I had the same problem, but I recognised that height constrained has been set correctly. So I did the following:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {

    CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    if (requiredHeight.size.width > UITextviewWidth) {
        requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);

    return requiredHeight.size;
answered Oct 6, 2015 by rajeshujade
0 votes

One thing I was noticing is that the rect that would come back from (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context would have a larger width than what I passed in. When this happened my string would be truncated. I resolved it like this:

NSString *aLongString = ...
NSInteger width = //some width;            
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
                                     attributes:@{ NSFontAttributeName : font,
                                                   NSForegroundColorAttributeName : [UIColor whiteColor]}

if(rect.size.width > width)
    return rect.size.height + font.lineHeight;
return rect.size.height;
answered Oct 6, 2015 by loknath.ganji
0 votes
    NSAttributedString *attributedText =[[[NSAttributedString alloc]
                                          attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];

    CGRect paragraphRect =
    [attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
    contentSize = paragraphRect.size;


if label's frame not add 10 this method will never work! hope this can help you! goog luck.

answered Oct 6, 2015 by mca.agarwal
0 votes

I'd like to add my thoughts since I had exactly the same problem.

I was using UITextView since it had nicer text alignment (justify, which at the time was not available in UILabel), but in order to "simulate" non-interactive-non-scrollable UILabel, I'd switch off completely scrolling, bouncing, and user interaction.

Of course, problem was that text was dynamic, and while width would be fixed, height should be recalculated every time I'd set new text value.

boundingRectWithSize didn't work well for me at all, from what I could see, UITextView was adding some margin on top which boundingRectWithSize would not get into a count, hence, height retrieved from boundingRectWithSize was smaller than it should be.

Since text was not to be updated rapidly, it's just used for some information that may update every 2-3 seconds the most, I've decided following approach:

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
    CGFloat msgWidth = tv.frame.size.width; // get target's width

    // Make "test" UITextView to calculate correct size
    UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
    // Set all font and text related parameters to be exact as the ones in targeted text view
    [temp setFont:tv.font];
    [temp setTextAlignment:tv.textAlignment];
    [temp setTextColor:tv.textColor];
    [temp setText:text];

    // Ask for size that fits :P
    CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];

    // kill this "test" UITextView, it's purpose is over
    [temp release];
    temp = nil;

    // apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
    tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );

*Above code is not directly copied from my source, I had to adjust it / clear it from bunch of other stuff not needed for this article. Don't take it for copy-paste-and-it-will-work-code.

Obvious disadvantage is that it has alloc and release, for each call.

But, advantage is that you avoid depending on compatibility between how boundingRectWithSize draws text and calculates it's size and implementation of text drawing in UITextView (or UILabel which also you can use just replace UITextView with UILabel). Any "bugs" that Apple may have are this way avoided.

P.S. It would seem that you shouldn't need this "temp" UITextView and can just ask sizeThatFits directly from target, however that didn't work for me. Though logic would say it should work and alloc/release of temporary UITextView are not needed, it did not. But this solution worked flawlessly for any text I would set in.

answered Oct 6, 2015 by tejas lakhani
0 votes
textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

Works on all fonts perfectly!

answered Oct 6, 2015 by abhi
0 votes
    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [UIFont systemFontOfSize:18], NSFontAttributeName,
                                      [UIColor blackColor], NSForegroundColorAttributeName,

    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
    myLabel.attributedText = attributedString; //this is the key!

    CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);

    CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
                                                    attributes:stringAttributes context:nil];

    self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

I tried everything on this page and still had one case for a UILabel that was not formatting correctly. Actually setting the attributedText on the label finally fixed the problem.

answered Oct 6, 2015 by ankitarajoria4
0 votes

I didn't have luck with any of these suggestions. My string contained unicode bullet points and I suspect they were causing grief in the calculation. I noticed UITextView was handling the drawing fine, so I looked to that to leverage its calculation. I did the following, which is probably not as optimal as the NSString drawing methods, but at least it's accurate. It's also slightly more optimal than initialising a UITextView just to call -sizeThatFits:.

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];

const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);
answered Oct 6, 2015 by mannar kande