*Accurately* calculating text height in Cocoa (for Mac, not iOS)

I solved the problem using the NS(Attributed)String+Geometrics category provided at http://www.sheepsystems.com/sourceCode/sourceStringGeometrics.html Within the header file of that category is a good explanation of how these things work. Specifically, they mention that one should use NSTextView instead of NSTextField because it's virtually impossible to get an accurate height for the latter.

Thus, I replaced my NSTextField with NSTextView. First, I made my NSTextView look like my NSTextField with this code:

[textView setEditable:YES];
[textView insertText:someString];
[textView setFont:[NSFont fontWithName:@"Lucida Grande"
   size:11] range:NSMakeRange(0,[someString length])];
[textView setEditable:NO];
[textView setSelectable:NO];

Then I got the height with this code:

NSDictionary *attributes
  = [NSDictionary dictionaryWithObjectsAndKeys:
     [textView font],NSFontAttributeName,nil];
NSAttributedString *attributedString
  = [[NSAttributedString alloc] initWithString:
     [textView string] attributes:attributes];
CGFloat height = [attributedString heightForWidth:
                 [textView frame].size.width];

The height does indeed turn out to be accurate for NSTextView.

I may have omitted some minor details, but you get the picture. I hope this helps someone out there.


(a solution in Swift is at the end of the post)

I tried all of these methods and none worked for me. The 'official' way (as mentioned by OP) was off most of the time for my NSTextField. Switching to NSTextView was impossible because of XCode/IB always crashing after adding a "NSTextView in an NSScrollView".

Long story short, I found this easy way to determine the height of my NSTextField but this can be very easily adapted to width.

CGFloat minHeight = [((NSTextFieldCell *)[yourTextField cell]) cellSizeForBounds:NSMakeRect(0, 0, YOUR_MAX_WIDTH, FLT_MAX)].height;

This code has worked for every single test-case that failed with the other methods. I hope this helps someone. It always makes me feel so darn stupid when I take 2 hours to find the solution to a seemingly easy problem.

SLIGHT EDIT: Upon closer inspection this only works if you read the stringValue of the NSTextField before I ask the cell to calculate its height. So I added the following nonsensical line before the actual calculation:

[yourTextField setStringValue:yourTextField.stringValue];

I know this is really really bad but at this point I don't care anymore. I just want a reliable solution. Feel free to downvote me or better yet: suggest a better solution.


Solution in Swift (thank you iphaaw for bringing this to my attention!)

myTextField.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), width, CGFloat(FLT_MAX))).height

Thanks indeed to iphaaw and Enchilada for this really cool little solution. I found it worked so well at setting row heights etc that I converted iphaaw's Swift into a couple of NSTextField extensions. Enjoy

extension NSTextField {
func bestheight(text: String, width: CGFloat) -> CGFloat {

    self.stringValue = text
    let getnumber = self.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), width, CGFloat(FLT_MAX))).height

    return getnumber
   }
}

extension NSTextField {
func bestwidth(text: String, height: CGFloat) -> CGFloat {

    self.stringValue = text
    let getnumber = self.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), CGFloat(FLT_MAX), height)).width

    return getnumber
   }
}

I hope someone finds these helpful. regards KGH