How to get height for NSAttributedString at a fixed width

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


I have a complex attributed string with multiple fonts and got incorrect results with a few of the above answers that I tried first. Using a UITextView gave me the correct height, but was too slow for my use case (sizing collection cells). I wrote swift code using the same general approach described in the Apple doc referenced previously and described by Erik. This gave me correct results with must faster execution than having a UITextView 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
}

-[NSAttributedString boundingRectWithSize:options:]

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

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

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

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