I want to do some drawing of NSAttributedStrings in fixed-width boxes, but am having trouble calculating the right height they\'ll take up when drawn. So far, I\'ve tried:
You might be interested in Jerry Krinock's great (OS X only) NS(Attributed)String+Geometrics category, which is designed to do all sorts of string measurement, including what you're looking for.
I just wasted a bunch of time on this, so I'm providing an additional answer to save others in the future. Graham's answer is 90% correct, but it's missing one key piece:
To obtain accurate results with -boundingRectWithSize:options:
you MUST pass the following options:
NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading
If you omit the lineFragmentOrigin one, you'll get nonsense back; the returned rect will be a single line high and won't at all respect the size you pass into the method.
Why this is so complicated and so poorly documented is beyond me. But there you have it. Pass those options and it'll work perfectly (on OS X at least).
On OS X 10.11+, the following method works for me (from Apple's Calculating Text Height document)
- (CGFloat)heightForString:(NSAttributedString *)myString atWidth:(float)myWidth
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:myString];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:
NSMakeSize(myWidth, FLT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
[layoutManager glyphRangeForTextContainer:textContainer];
return [layoutManager
usedRectForTextContainer:textContainer].size.height;
}
Swift 4.2
let attributedString = self.textView.attributedText
let rect = attributedString?.boundingRect(with: CGSize(width: self.textView.frame.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
print("attributedString Height = ",rect?.height)
Use NSAttributedString method
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context
The size is the constraint on the area, the calculated area width is restricted to the specified width whereas the height is flexible based on this width. One can specify nil for context if that's not available. To get multi-line text size, use NSStringDrawingUsesLineFragmentOrigin for options.
Not a single answer on this page worked for me, nor did that ancient old Objective-C code from Apple's documentation. What I finally did get to work for a UITextView
is first setting its text
or attributedText
property on it and then calculating the size needed like this:
let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))
Works perfectly. Booyah!