63

Possible Duplicate:
How to get height for NSAttributedString at a fixed width

Now NSAttributedString is available in iOS 6. For layout purposes, I want to know how to calculate the required height of an NSAttributedString under fixed width. I'm looking for something that's equivalent to NSString's - (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size but for NSAttributedString.

To calculate the drawing size of NSAttributedStrings, there are two methods available:

  1. - (CGSize)size can't be used because it does not take any width into consideration.
  2. I tried - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context, but somehow it doesn't give me the correct height. I think the method is buggy. If I run the following code, it gives me bounding size: 572.324951, 19.000000 ignoring the given width of 200. It should give me something like 100 of height.
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
    NSDictionary *attributes = @{NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue" size:15], NSForegroundColorAttributeName : [UIColor blueColor]};
    [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"Attributed String\n" attributes:attributes]];
    [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"Attributed String\n" attributes:attributes]];
    [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"Attributed String\n" attributes:attributes]];
    [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"Attributed String\n" attributes:attributes]];
    [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"Attributed String\n" attributes:attributes]];

    CGRect frame = [attributedString boundingRectWithSize:CGSizeMake(200, 1000) options:0 context:nil];
    NSLog(@"bounding size: %f, %f", frame.size.width, frame.size.height);

There are other methods available for Mac OS X, but not for iOS.

Community
  • 1
  • 1
Jake
  • 1,518
  • 2
  • 14
  • 20
  • 9
    @SimonGoldeen That's not a good duplicate. The accepted answer doesn't actually give the height and the other answers don't show the proper way to use the `boundingRectWithSize:options:context:` method. – rmaddy Jan 19 '13 at 01:01
  • 1
    In Swift, this can be done by `let desiredWidth: CGFloat = 300; let rect = attrStr.boundingRect(with: CGSize(width: desiredWidth, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)` – Jake Jan 10 '19 at 06:11

1 Answers1

179

Option 2 does work in iOS with the proper parameters.

NSAttributedString *attrStr = ... // your attributed string
CGFloat width = 300; // whatever your desired width is
CGRect rect = [attrStr boundingRectWithSize:CGSizeMake(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];

Without the proper values for the options parameter you will get the wrong height.

It is also required that attrStr contains a font attribute. Without a font, there is no way to properly calculate the size.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    your code works. Thanks a lot! – Jake Jan 19 '13 at 02:14
  • 29
    Hmm... not working for me. I seem to get good calculations about only half the time. – Dogweather Mar 15 '13 at 09:01
  • 1
    It does not work for me either. It is way off like it is not accounting for font size. – Brennan Aug 23 '13 at 22:33
  • 3
    Finally a simple and functional way to calculate the size of a `NSAttributedString` that works even with very complex formatting. Other answers to similar questions go as far as creating a category on NSAttributedString - thankfully it does not have to be that hard. – SaltyNuts Sep 23 '13 at 15:06
  • 38
    @Dogweather In my experience, the NSAttributedString must have a NSFontAttributeName key to be sized correctly. – bentford Jan 09 '14 at 22:46
  • 2
    @bentford THANK YOU for that tip - I was creating an attributed string with the -initWithString: method, and that resulted in completely arbitrary computed sizes. Using -initWithString:attributes: works! – David Ganster Aug 08 '14 at 11:20
  • 1
    See my answer for how I worked around a problem where boundingRectWithSize on UILabel's attributedText would always return the height for a single line http://stackoverflow.com/a/28226095/115676 – AlexD Jan 29 '15 at 22:46
  • 1
    I have discovered an interesting text pattern that always results in an incorrect height measurement: some text containing "[]" followed by a singular, soft-returned word (that triggers an implicit word-wrap) – Daniel Randall Apr 21 '15 at 15:27
  • 28
    We probably want to use `CGFLOAT_MAX` for the size parameter, rather than `10000` which, at some point in time, might actually fit on our screens... – nhgrif Jun 08 '15 at 13:04
  • Can't set an attributedString before BoundingRectWithSize it gives me No visible @interface blahblah. How does this work? – durazno Aug 13 '15 at 08:31
  • Hii... i also getting some time perfect height but not all time.. when my string is too large at that time i am getting wrong height – Anny Dec 19 '15 at 05:37
  • If set lineSpacing, it did not get the correct height. – len Mar 01 '16 at 11:05
  • It works depend on the text. It doesn't work for all cases. [textView sizeThatFits:] seems to work better. But always adds an extra space at the end – Gabriel Goncalves Apr 15 '16 at 16:32
  • The results arent reliable on screen. – Karsten Dec 13 '18 at 13:19
  • @AlexD to add to this old thread, use `usesLineFragmentOrigin` to make it multi-line – danqing Jul 08 '19 at 11:13