使用自动布局自动换行NSTextField

ken*_*nyc 8 cocoa appkit autolayout

当NSTextField的宽度发生变化时,如何让自动布局自动将NSTextField包装到多行?

我有许多NSTextFields在检查器窗格中显示静态文本(即:标签).当用户调整检查器窗格的大小时,如果需要,我希望右侧标签能够回流到多行.

(Finder的"获取信息"面板执行此操作.)

但是我无法弄清楚自动布局约束的正确组合以允许这种行为.在所有情况下,右侧的NSTextFields拒绝换行.(除非我明确添加允许它的高度约束.)

视图层次结构使得每个灰色条带是包含两个NSTextField的视图,左侧是属性名称,右侧是属性值.当用户调整检查器窗格的大小时,我希望属性值标签根据需要自动调整其高度.

现在的情况:

文字字段不包装.

我希望发生什么:

在此输入图像描述

(请注意,这种行为与我遇到的关于NSTextFields和自动布局的大多数Stack Overflow问题不同.这些问题希望文本字段在用户输入时增长.在这种情况下,文本是静态的,NSTextField配置为看起来像一个标签.)

更新1.0

考虑到@ hamstergene的建议,我将NSTextField子类化并制作了一个示例应用程序.在大多数情况下,它现在可以工作,但现在有一个小的布局问题,我怀疑这是因为NSTextField的框架与自动布局所期望的完全不同步.在下面的屏幕截图中,右侧标签都是垂直间隔的top约束.在调整窗口大小时,Where字段将正确调整大小并进行包装.但是,在Kind我再次调整窗口"一个像素"之前,文本字段不会被推下.

在此输入图像描述 示例:如果我将窗口的大小调整为Where文本字段首次换行的正确宽度,那么我会在中间图像中得到结果.如果我再向窗口调整一个像素,则会Kind正确设置字段的垂直位置.

我怀疑这是因为自动布局正在通过,然后帧被明确设置.我想自动布局在那个传球上没有看到,但是在下一次传球时看到它,并相应地更新位置.

假设这是问题,我如何通知我正在进行的这些更改的自动布局,setFrameSize以便它可以再次运行布局.(而且,最重要的是,不要陷入layout-setFrameSize-layout-etc.的递归状态...)

我想出了一个解决方案,似乎与我希望的完全一致.取而代之的子类的NSTextField,我只是覆盖layoutsuperviewNSTextField问题.在内部layout,我设置preferredMaxLayoutWidth文本字段,然后触发布局传递.这似乎足以使它大部分工作,但它留下了布局短暂"错误"的恼人问题.(见上面的注释).

解决方案似乎是调用setNeedsDisplay然后一切正常工作.

    - (void)layout {
      NSTextField *textField = ...;
      NSRect oldTextFieldFrame = textField.frame;

      [textField setPreferredMaxLayoutWidth:NSWidth(self.bounds) - NSMinX(textField.frame) - 12.0];

      [super layout];

      NSRect newTextFieldFrame = textField.frame;
      if (oldTextFieldFrame.size.height != newTextFieldFrame.size.height) {
        [self setNeedsDisplay:YES];
      }
    }
Run Code Online (Sandbox Code Playgroud)

Ben*_*man 7

假设您正在使用NSViewController基于解决方案的解决方案,最简单的方法是:

- (void)viewDidLayout {
    [super viewDidLayout];
    self.aTextField.preferredMaxLayoutWidth = self.aTextField.frame.size.width;
    [self.view layoutSubtreeIfNeeded];
}
Run Code Online (Sandbox Code Playgroud)

这简单地让约束系统求解宽度(高度将在此运行中无法解析,因此将是您最初设置的高度),然后将该宽度应用为最大布局宽度并执行另一个基于约束的布局传递.

没有子类化,没有视图的布局方法,没有通知.如果你没有使用,NSViewController你可以调整这个解决方案,以便它在大多数情况下工作(子类化文本字段,在自定义视图中等).

其中大部分来自于膨胀http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html(查看多行文本内在内容大小部分).


ham*_*ene 6

如果检查器窗格宽度永远不会改变,只需检查IB中的"First Runtime Layout Width"(注意它的10.8+特性).

但允许检查员同时具有可变宽度是不可能仅通过约束来实现的.关于这一点,AutoLayout中有一个弱点.

通过子类化文本字段,我能够实现可靠的行为:

- (NSSize) intrinsicContentSize;
{
    const CGFloat magic = -4;

    NSSize rv;
    if ([[self cell] wraps] && self.frame.size.height > 1)
        rv = [[self cell] cellSizeForBounds:NSMakeRect(0, 0, self.bounds.size.width + magic, 20000)];
    else
        rv = [super intrinsicContentSize];
    return rv;
}

- (void) layout;
{
    [super layout];
    [self invalidateWordWrappedContentSizeIfNeeded];
}

- (void) setFrameSize:(NSSize)newSize;
{
    [super setFrameSize:newSize];
    [self invalidateWordWrappedContentSizeIfNeeded];
}

- (void) invalidateWordWrappedContentSizeIfNeeded;
{
    NSSize a = m_previousIntrinsicContentSize;
    NSSize b = self.intrinsicContentSize;
    if (!NSEqualSizes(a, b))
    {
        [self invalidateIntrinsicContentSize];
    }
    m_previousIntrinsicContentSize = b;
}
Run Code Online (Sandbox Code Playgroud)

在任何一种情况下,必须以明显的方式设置约束(您可能已经尝试过):高垂直拥抱优先级,低水平,将所有四个边都固定到superview和/或兄弟视图.