[ACCEPTED]-How to get height for NSAttributedString at a fixed width-nsattributedstring

Accepted answer
Score: 31
-[NSAttributedString boundingRectWithSize:options:]

You can specify NSStringDrawingUsesDeviceMetrics to get union of all glyph bounds.

Unlike -[NSAttributedString size], the returned 7 NSRect represents the dimensions of the area that 6 would change if the string is drawn.

As @Bryan 5 comments, boundingRectWithSize:options: is deprecated (not recommended) in 4 OS X 10.11 and later. This is because string 3 styling is now dynamic depending on the 2 context.

For OS X 10.11 and later, see Apple's 1 Calculating Text Height developer documentation.

Score: 14

The answer is to use
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
but the rect you pass in 3 should have 0.0 in the dimension you want 2 to be unlimited (which, er, makes perfect 1 sense). Example here.

Score: 13

I have a complex attributed string with 10 multiple fonts and got incorrect results 9 with a few of the above answers that I tried 8 first. Using a UITextView gave me the correct 7 height, but was too slow for my use case 6 (sizing collection cells). I wrote swift 5 code using the same general approach described 4 in the Apple doc referenced previously and described 3 by Erik. This gave me correct results with 2 must faster execution than having a UITextView 1 do the calculation.

private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
    let ts = NSTextStorage(attributedString: str)

    let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)

    let tc = NSTextContainer(size: size)
    tc.lineFragmentPadding = 0.0

    let lm = NSLayoutManager()
    lm.addTextContainer(tc)

    ts.addLayoutManager(lm)
    lm.glyphRange(forBoundingRect: CGRect(origin: .zero, size: size), in: tc)

    let rect = lm.usedRect(for: tc)

    return rect.integral.size.height
}
Score: 5

You might be interested in Jerry Krinock's 3 great (OS X only) NS(Attributed)String+Geometrics category, which is designed 2 to do all sorts of string measurement, including 1 what you're looking for.

Score: 3

On OS X 10.11+, the following method works 1 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;
}
Score: 3

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)

0

Score: 2

I just wasted a bunch of time on this, so 12 I'm providing an additional answer to save 11 others in the future. Graham's answer is 10 90% correct, but it's missing one key piece:

To 9 obtain accurate results with -boundingRectWithSize:options: you MUST pass the following options:

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading

If you omit 8 the lineFragmentOrigin one, you'll get nonsense 7 back; the returned rect will be a single 6 line high and won't at all respect the size 5 you pass into the method.

Why this is so 4 complicated and so poorly documented is 3 beyond me. But there you have it. Pass those 2 options and it'll work perfectly (on OS 1 X at least).

Score: 2

Swift 3:

let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
        NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
        NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
])

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
placeholderTextView.attributedText = attributedStringToMeasure
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height

This answer works great for me, unlike 4 the other ones which were giving me incorrect 3 heights for larger strings.

If you want 2 to do this with regular text instead of 1 attributed text, do the following:

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
placeholderTextView.text = "Some text"
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height
Score: 1

Use NSAttributedString method

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context

The size is 7 the constraint on the area, the calculated 6 area width is restricted to the specified 5 width whereas the height is flexible based 4 on this width. One can specify nil for context 3 if that's not available. To get multi-line 2 text size, use NSStringDrawingUsesLineFragmentOrigin 1 for options.

Score: 1

As lots of guys mentioned above, and base 6 on my test.

I use open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect on iOS like this bellow:

let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)

Here 5 the 200 is the fixed width as your expected, height I give it 0 since I think it's 4 better to kind tell API height is unlimited.

Option 3 is not so important here,I have try .usesLineFragmentOrigin or 2 .usesLineFragmentOrigin.union(.usesFontLeading) or .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), it give same result.

And the result 1 is expected as my though.

Thanks.

Score: 0

Not a single answer on this page worked 5 for me, nor did that ancient old Objective-C 4 code from Apple's documentation. What I finally did get to work 3 for a UITextView is first setting its text or attributedText property 2 on it and then calculating the size needed 1 like this:

let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))

Works perfectly. Booyah!

Score: 0

I found helper class to find height and width of attributedText (Tested code)

https://gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292

Add above file in your project

How to use

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")

0

More Related questions