自动换行的NSTextField使用自动布局

问题描述 投票:8回答:4

如何去大约有自动布局自动包装一个的NSTextField多行作为的变化的NSTextField的宽度?

我有许多NSTextFields显示在检查窗格静态文本(即:标签)。当检查员窗格被用户调整,我想右侧标签回流到多条线路如果需要的话。

(Finder的获取信息面板做到这一点。)

但我一直无法弄清楚的自动布局约束的组合,以允许这种行为。在所有的情况下,在右边的NSTextFields拒绝包裹。 (除非我明确地添加一个高度约束,将允许它。)

视图层次结构是这样的,每一个灰色带是含有两个NSTextFields,左侧的属性名称和右边的属性值的图。当用户调整检查窗格,我想属性值标签自动调整它的高度需求,是。

现在的情况:

我想有发生:

(请注意,这种行为比大多数堆栈溢出的问题不同,我碰到关于NSTextFields和经销商的布局。这些问题要在文本字段增长,而用户键入。在这种情况下,该文本是静态的和的NSTextField配置看就像一个标签。)

1.0更新

以@ hamstergene的建议下,我子类的NSTextField,并提出了小样本应用程序。在大多数情况下,它现在的作品,但现在有,我怀疑是不是在完全同步作为的NSTextField的帧的结果什么自动布局希望它是一个小的布局问题。在下面的屏幕截图中,右手侧的标签都被垂直地一个top约束隔开。随着窗口的大小时,Where场得到适当调整和包裹。然而,Kind文本字段不被推下来,直到我调整窗口“多了一个像素”。

例如:如果我调整窗口的大小,以恰到好处的宽度,该文本框Where做它的第一个包,然后我得到的中间图像中的结果。如果我调整窗口,多了一个像素,则该Kind领域的垂直位置设置正确。

我怀疑这是因为自动布局是这样做的传球,然后框架越来越明确设置。我想象自动布局没有看到对通,但确实是它的下传,并相应地更新位置。

假设这是个问题,我怎么告诉这些改变我做的setFrameSize的自动布局,以便它可以再次运行布局。 (而且,最重要的是,不会被抓到在递归状态布局setFrameSize布局,等...)

我想出了,似乎我的工作究竟是如何希望的解决方案。相反子类NSTextField的,我只是覆盖layout在有关superviewNSTextField。在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];
      }
    }
cocoa autolayout appkit
4个回答
6
投票

如果检查面板的宽度不会改变,只是检查“首先运行版式宽度”在IB(注意这是10.8+功能)。

但允许检查员在同一时间有变化宽度不可能实现单独的约束。有一个薄弱点自动版式某处对此。

我能够通过继承这样的文本字段,以实现可靠的行为:

- (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;
}

在这两种情况下,约束必须设置明显的方式(你可能已经尝试过):高垂直拥抱优先级低的水平,销全部四个边缘,上海华盈和/或姊妹的意见。


7
投票

最简单的方法来得到这个工作,假设你使用的是基于NSViewController的解决方案是这样的:

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

这只是让约束系统解决宽度(高度将在此运行无法解决这样会什么都你最初把它设置为),那么您应用的宽度,最大宽度布局,做一套基于约束的布局传递。

没有子类,没有与视图的布局方法,没有通知搞混。如果你不使用NSViewController使其作品在大多数情况下(子类文本字段,自定义视图等),你可以调整这个解决方案。

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


1
投票

设置在尺寸检查标签在部分文本字段首选宽度设置为“首先运行版式宽度”


0
投票

这个工作对我来说,是有点更优雅。此外,我所做的Github一个小样本项目

public class DynamicTextField: NSTextField {
   public override var intrinsicContentSize: NSSize {
      if cell!.wraps {
         let fictionalBounds = NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
         return cell!.cellSize(forBounds: fictionalBounds)
      } else {
         return super.intrinsicContentSize
      }
   }

   public override func textDidChange(_ notification: Notification) {
      super.textDidChange(notification)

      if cell!.wraps {
         validatingEditing()
         invalidateIntrinsicContentSize()
      }
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.