Vasily Vasily - 6 months ago 143
Swift Question

NSTextField resizable to fit content

I have NSTextField with left, right and top constraints defined (no bottom constraint set). I need NSTextField to grow if content can't fit in it and decrease size if there is unused space left.

Now I have: NSTextField automatically expands with strange behavior if it has multi-line text or too much content, also NSTextField doesn't decrease own's size on window resize.

I haven't found any simple solution written on Swift to solve that problem (I have a lot of such labels with constraints), at iOS everything was working with usual text labels and constraints.

Illustration

I've created simple project for that question that you can see the problem: [Download Text.zip]

The solutions I've found but not used:


  1. You can try to calculate possible TextField height and set height constraint for it. Problems of that solution:


    • Possible height calculations are inaccurate, sometimes you calculate incorrect height.

    • Solutions are written on Objective-C with some complex code.


  2. Run .sizeToFit() on each window resize or text change action. It's not working because .sizeToFit() always compress all text to single line.

  3. Use NSTextView instead of NSTextField. It's good way, but:


    • I don't need scrolling, editing and other functional of NSTextView. I don't want to call to complex component for simple label.

    • NSTextView always wants height or bottom constraint, I don't know bottom constraint because content can expand down with new text.

    • I haven't find full solution to make NSTextView's behavior like I want :)



Answer

According to Mac OS X Release Notes (section NSTextField intrinsicContentSize improvements) it’s known bug when height of NSTextField is changed, but width is remained the same. We have two ways to fixing it:

  1. We can specify maximumNumberOfLines to a value that makes sense. That is not good way to us because we don’t know actual number of lines and don’t want to calculate it.
  2. We can set preferredMaxLayoutWidth to a real value. I’ve ended with such code:

Code:

override func viewDidLayout() {
    super.viewDidLayout()
    textField.preferredMaxLayoutWidth = textField.frame.width
}