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

前端 未结 6 1214
一向
一向 2020-12-14 04:32

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 fr

相关标签:
6条回答
  • 2020-12-14 04:37

    Take a look at the NSString Application Kit Additions Reference. It seems like – sizeWithAttributes: or – boundingRectWithSize:options:attributes: could do what you want.

    0 讨论(0)
  • 2020-12-14 04:47

    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

    0 讨论(0)
  • 2020-12-14 04:48

    Vigorouscoding's answer works so well! Is this really bad? It seems to work perfectly. Finally after spending many hours of trying other solutions.

    Here is my Swift translation of the code:

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

    Please don't up vote this answer. Upvote vigorouscoding's entry instead.

    0 讨论(0)
  • 2020-12-14 04:50

    (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
    
    0 讨论(0)
  • 2020-12-14 05:01

    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.

    0 讨论(0)
  • 2020-12-14 05:04

    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
        }
    }
    
    0 讨论(0)
提交回复
热议问题