NSString boundingRectWithSize returning unnecessarily tall height

You might want to use NSStringDrawingUsesDeviceMetrics in the options. From the docs:

NSStringDrawingUsesDeviceMetrics

Use the image glyph bounds (instead of the typographic bounds) when computing layout.


@omz still gets credit for making this work for me. His answer made me look around CoreText some more since I assume something like boundingRectWithSize ultimately calls a CoreText function.

In Session 226 from WWDC 2012 there was an entire section devoted to calculating the metrics of a line and, to my surprise, they talked about a new CoreText function called CTLineGetBoundsWithOptions.

As far as I can tell, that method is only documented in CTLine.h and in the CoreText Changes document. It certainly doesn't come up (for me) when doing a normal search in the documentation.

But it appears to work and in my testing it returns the exact same result as boundingRectWithSize for all the fonts installed on my system. Even better, is that it appears to be almost 2x faster than boundingRectWithSize.

As the WWDC video mentions, it's a bit obscure why you'd need to calculate the bounds of a string without taking things like line height into account, but if you do need to do that, then I think this might be the best method to use. As always, YMMV.

Rough sample code:

NSFont *font = [NSFont systemFontOfSize:13.0];
NSDictionary *attributes = @{(__bridge NSString *)kCTFontAttributeName : font};
NSAttributedString *attributeString = [[NSAttributedString alloc] initWithString:self.text attributes:attributes];

CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attributeString);
CGRect bounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsUseGlyphPathBounds);

CFRelease(line);

return NSRectFromCGRect(bounds);