Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTextField width and autolayout

I am trying to create an NSTextField programmatically.

I want to use this NSTextField with auto layout, so its width will be defined automatically to display the entire line (there is only one line of text).

The problem is that textField.intrinsicContentSize and textField.fittingSize are both have -1 and 0 values for the horizontal coordinate, as the output of the code below is: textField.intrinsicContentSize={-1, 21} textField.fittingSize={0, 21}

The code:

NSTextField* textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 24)];    
NSString* str = @"One line of text here.";
[textField setStringValue: str];
[textField setTranslatesAutoresizingMaskIntoConstraints:NO];
[textField invalidateIntrinsicContentSize];

NSLog(@"textField.intrinsicContentSize=%@", NSStringFromSize(textField.intrinsicContentSize));
NSLog(@"textField.fittingSize=%@", NSStringFromSize(textField.fittingSize));

This makes the text field to have a zero width assigned by auto layout.

What should I do to get meaningful values for the fittingSize and intrinsicContentSize properties of the text field so they reflect the content of the text field?

like image 582
Yoav Avatar asked Feb 01 '13 09:02

Yoav


2 Answers

Another way to compute the text field optimal width without using fittingSize is using the following code (replacing John Sauer's sizeTextFieldWidthToFit method):

- (void) sizeTextFieldWidthToFit {
    [textField sizeToFit];
    CGFloat newWidth = NSWidth(textField.frame);
    textFieldWidthConstraint.constant = newWidth;
}

You can set the priority of textFieldWidthConstraint to be lower than the priority of another inequality constrain the represent your requirement to a minimal size of 25.

like image 142
Yoav Avatar answered Sep 18 '22 13:09

Yoav


I can't explain why NSTextField's intrinsicContentSize's widths are -1, but I was able to calculate one based off its attributedString's size. Here's how I created the NSTextField:

// textField is an instance variable.
textField = [NSTextField new] ;
textField.translatesAutoresizingMaskIntoConstraints = NO ;
[view addSubview:textField] ;

NSDictionary* viewsDict = NSDictionaryOfVariableBindings(view, textField) ;
// textFieldWidthConstraint is an instance variable.
textFieldWidthConstraint  = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[textField(==0)]"  options:0  metrics:nil  views:viewsDict] [0] ;
[view addConstraint:textFieldWidthConstraint] ;
NSNumber* intrinsicHeight = @( textField.intrinsicContentSize.height ) ;
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[textField(==intrinsicHeight)]"  options:0  metrics:NSDictionaryOfVariableBindings(intrinsicHeight)  views:viewsDict]] ;

// Position textField however you like.
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[textField]"  options:0  metrics:nil  views:viewsDict]] ;
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[textField]"  options:0  metrics:nil  views:viewsDict]] ;

[self sizeTextFieldWidthToFit] ;
// set textField's delegate so that  sizeTextFieldWidthToFit  is called when textField's text changes.
textField.delegate = self ;

And here are the methods that resize it as its text changes:

- (void) controlTextDidChange:(NSNotification*)aNotification {
    if ( aNotification.object == textField )
        [self sizeTextFieldWidthToFit] ;
}

- (void) sizeTextFieldWidthToFit {
    // I'd like the width to always be >= 25.
    CGFloat newWidth = 25 ;
    if ( ! [textField.stringValue isEqualToString:@""] ) {
        NSDictionary* attributes = [textField.attributedStringValue  attributesAtIndex:0  effectiveRange:nil] ;
        NSSize size = [textField.stringValue sizeWithAttributes:attributes] ;
        newWidth =  MAX(newWidth, size.width + 10) ;
    }
    textFieldWidthConstraint.constant  = newWidth  ;
}
like image 36
John Sauer Avatar answered Sep 20 '22 13:09

John Sauer