1

Is there any way to automatically resize the height of a NSTokenField (keeping the width constant) using constraints?

-sizeToFit should work, but it doesn't. If I set a constraint to keep the width constant and call this method it ignores the constraints and resizes the width only (when what I want is to resize the height only).

Alex
  • 5,009
  • 3
  • 39
  • 73

3 Answers3

1

Based on How to let NSTextField grow with the text in auto layout?

Also don't set size contraints, just let it be.

The method intrinsicContentSize in NSView returns what the view itself thinks of as its intrinsic content size.

NSTextField calculates this without considering the wraps property of its cell, so it will report the dimensions of the text if laid out in on a single line.

Hence, a custom subclass of NSTokenField can override this method to return a better value, such as the one provided by the cell's cellSizeForBounds: method:

-(NSSize)intrinsicContentSize
{
    if ( ![self.cell wraps] ) {
        return [super intrinsicContentSize];
    }

    NSRect frame = [self frame];

    CGFloat width = frame.size.width;

    // Make the frame very high, while keeping the width
    frame.size.height = CGFLOAT_MAX;

    // Calculate new height within the frame
    // with practically infinite height.
    CGFloat height = [self.cell cellSizeForBounds: frame].height;

    return NSMakeSize(width, height);
}
Community
  • 1
  • 1
aryeh
  • 2,195
  • 1
  • 16
  • 23
  • 1
    not quite, the cellSizeForBounds returns always the same size in tokenfield... also you have to listen on textDidChange and invalidateIntrinsicContentSize – Peter Lapisu Oct 04 '14 at 19:12
  • @PeterLapisu cellSizeForBounds does not always return the same size, at least on 10.11. I've posted an answer with the full code. – rdougan Sep 20 '15 at 11:26
1

The cellSizeForBounds method of the token field does return the correct size, so you can implement it like this (custom subclass, in Swift):

class TagsTokenField: NSTokenField {

    override func textDidChange(notification: NSNotification) {
        super.textDidChange(notification)
        self.invalidateIntrinsicContentSize()
    }

    override var intrinsicContentSize: NSSize {
        let size = self.cell!.cellSizeForBounds(NSMakeRect(0, 0, self.bounds.size.width, 1000))
        return NSMakeSize(CGFloat(FLT_MAX), size.height)
    }

}
rdougan
  • 7,217
  • 2
  • 34
  • 63
0

Needing an NSTokenField with a given width that dynamically grows in height when adding tokens, as well as is displayed with the right size when initialized not empty, I came up with the following super simple solution that works flawless for me:

- (NSSize)intrinsicContentSize {
    return [self sizeThatFits:NSMakeSize(self.frame.size.width, CGFLOAT_MAX)];
}

- (void)textDidChange:(NSNotification *)notification {
    [super textDidChange:notification];
    [self invalidateIntrinsicContentSize];
}

Because for some reason when the intrinsic content size of the NSTokenField is always calculated for a single line with unlimited width, we are constraining the width here to it's actual width. Guess we are lucky that sizeThatFits: works correctly.

Jan Z.
  • 6,883
  • 4
  • 23
  • 27