How to check if NSTextfield is already truncating the text (… at the end)

核能气质少年 提交于 2019-12-23 09:02:02

问题


I've searched around on how to perform this but I can't find any answer.
I'd like to know if my NSTextfield is already truncating the text (... at the end) without having to check the length of its stringValue. I need to do this to know if I should set a tooltip on my NSTextfield (acting like a simple label).
The reason I don't want to check the length of my textfield's stringValue it's because there are some characters that occupy more space than others, so that's not very accurate
Thanks!


回答1:


Your best bet might be to use an NSTextView instead of a NSTextField. If you use an NSTextView you can get the NSTextContainer of the NSTextView using the textContainer property. The container can tell you the containerSize (the space the text is drawn in).

NSString objects respond to the method sizeWithAttributes:. You can use the resulting NSSize struct to grab the width of the text if drawn with the given attributes. See the "Constants" section of the NSAttributedString Application Kit Additions Reference to figure out what attributes are relevant.

If the containerSize width is less than the sizeWithAttributes: width then the text will be truncated.

EDIT: Apologies, this is only true with no lineFragmentPadding, but the default lineFragmentPadding is non-zero. Either subtract the textContainer.lineFragmentPadding from containerSize.width or use the NSTextContainer setLineFragmentPadding: method to set it to 0.

I suppose you could also make some assumptions about the text area relative to the size of the NSTextField and use the sizeWithAttributes: NSString method in conjunction with that, but that is not as clean.

EDIT 2: I realized I did not address the OP's interest in truncating the text using ellipses. The example code below uses truncation in the NSTextView. I also thought I might as well throw in some code that makes the NSTextView a little more similar in appearance to the NSTextField by putting it inside of a NSBox. Adding a check for size to determine if a tooltip should be displayed would be a simple addition to the code below using the information already mentioned above.

NSString* string = @"Hello World.";

// get the size the text will take up using the default font
NSSize textBounds = [string sizeWithAttributes:@{}];

// Create a border view that looks more or less like the border used around
// text fields
NSBox* borderView = [[NSBox alloc] initWithFrame:NSMakeRect(10, 10, 60, textBounds.height+4)];
[borderView setBoxType:NSBoxCustom];
[borderView setBorderType:NSBezelBorder];
[borderView setContentViewMargins:NSMakeSize(0, 0)];
[borderView setFillColor:[NSColor whiteColor]];

// Create the text view
NSTextView* textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 60, textBounds.height)];
[textView setTextContainerInset:NSMakeSize(2, 0)];
[textView.textContainer setLineFragmentPadding:0];
[textView setEditable:YES];

// Set the default paragraph style so the text is truncated rather than
// wrapped
NSMutableParagraphStyle* parStyle = [[NSMutableParagraphStyle alloc] init];
[parStyle setLineBreakMode:NSLineBreakByTruncatingTail];

// Do not let text get squashed to fit
[parStyle setTighteningFactorForTruncation:0.0];

[textView setDefaultParagraphStyle:parStyle];
[parStyle release];

// Set text
[textView setString:string];

// add NSTextView to border view
[borderView addSubview:textView];
[textView release];

// add NSBox to view you want text view displayed in
[self addSubview:borderView];
[borderView release];



回答2:


For future visitors, you can use the -[NSCell expansionFrameWithFrame:inView:] method to determine if truncation is taking place. From the header:

Allows the cell to return an expansion cell frame if cellFrame is too small for the entire contents in the view. ...<snip>... If the frame is not too small, return an empty rect, and no expansion tool tip view will be shown. By default, NSCell returns NSZeroRect, while some subclasses (such as NSTextFieldCell) will return the proper frame when required.

In short, you can tell if an NSTextField is truncating like this:

    NSRect expansionRect = [[self cell] expansionFrameWithFrame: self.frame inView: self];
    BOOL truncating = !NSEqualRects(NSZeroRect, expansionRect);



回答3:


I think you can do it by looking at the frame of the text field's field editor. You check its width in controlTextDidBeginEditing, and then again in controlTextDidEndEditing. If the latter value is larger, then the text has been truncated. The following is implemented in the text field's delegate (I created an ivar for initialWidth):

- (void)controlTextDidBeginEditing:(NSNotification *)aNotification {
    if (! initialWidth) 
        initialWidth = ((NSTextView *)aNotification.userInfo[@"NSFieldEditor"]).frame.size.width;
}


- (void)controlTextDidEndEditing:(NSNotification *)aNotification {
    if (initialWidth) { //Make sure that beginEditing is called first
        NSInteger finalWidth = ((NSTextView *)aNotification.userInfo[@"NSFieldEditor"]).frame.size.width;
        if (finalWidth - initialWidth > 1) NSLog(@"We have truncation");
        NSLog(@"Final: %ld  Initial: %ld", finalWidth,initialWidth);
    }
}

This seems to work most of the time, including if you type in a long string, then delete until it fits again. In a few cases, it gave me the log message when I was one character past the end of the text field, but I did not get the ellipsis when editing ended.




回答4:


Swift 4 solution using ipmcc as base

It will resize one by one until it fit or fontsize = 3

var size_not_ok = true

var conter = 0
let mininum_font_size = 3 // will resize ultil 3 
while size_not_ok || conter < 15 { // will try 15 times maximun
    let expansionRect = le_nstextfield.expansionFrame(withFrame: le_nstextfield.frame)

    let truncated = !NSEqualRects(NSRect.zero, expansionRect)

    if truncated {

        if let actual_font_size : CGFloat = le_nstextfield.font?.fontDescriptor.object(forKey: NSFontDescriptor.AttributeName.size) as? CGFloat {

            le_nstextfield.font = NSFont.systemFont(ofSize: actual_font_size - 1)

            if actual_font_size < mininum_font_size {
                break
            } 
        }
    } else {        
        size_not_ok = false
    }
    conter  = conter + 1
}


来源:https://stackoverflow.com/questions/14430193/how-to-check-if-nstextfield-is-already-truncating-the-text-at-the-end

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!