How can I calculate the height of a string within a particular label (NSTextField), for some given fixed width?
I Googled up various methods and tried this method from Apple. It works, except that the height becomes one line too short for longer strings. Thus, there appear to be some inaccuracies in the present methods.
I also had the same issue with some other code I Googled up.
Does someone here have any accurate code for finding text height (given a particular width)?
(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
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.
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
This is ArtbyKGH's answer for Swift 4+:
extension NSTextField {
func bestHeight(for text: String, width: CGFloat) -> CGFloat {
stringValue = text
let height = cell!.cellSize(forBounds: NSRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude)).height
return height
}
func bestWidth(for text: String, height: CGFloat) -> CGFloat {
stringValue = text
let width = cell!.cellSize(forBounds: NSRect(x: 0, y: 0, width: .greatestFiniteMagnitude, height: height)).width
return width
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With