Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Cocoa 使用自动布局自动换行NSTextField_Cocoa_Autolayout_Appkit - Fatal编程技术网

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

Cocoa 使用自动布局自动换行NSTextField,cocoa,autolayout,appkit,Cocoa,Autolayout,Appkit,当NSTextField的宽度改变时,如何让自动布局自动将NSTextField包装成多行 在inspector窗格中,我有许多显示静态文本(即:标签)的NSTextFields。由于用户调整了inspector窗格的大小,如果需要,我希望右侧标签回流到多行 (查找者的“获取信息”面板会执行此操作。) 但是我还没有找到允许这种行为的自动布局约束的正确组合。在所有情况下,右侧的NSTEXT字段都拒绝换行。(除非我显式添加了允许其移动的高度约束。) 视图层次结构使得每个灰色标注栏都是一个包含两个NS

当NSTextField的宽度改变时,如何让自动布局自动将NSTextField包装成多行

在inspector窗格中,我有许多显示静态文本(即:标签)的NSTextFields。由于用户调整了inspector窗格的大小,如果需要,我希望右侧标签回流到多行

(查找者的“获取信息”面板会执行此操作。)

但是我还没有找到允许这种行为的自动布局约束的正确组合。在所有情况下,右侧的NSTEXT字段都拒绝换行。(除非我显式添加了允许其移动的高度约束。)

视图层次结构使得每个灰色标注栏都是一个包含两个NSTEXT字段的视图,属性名称位于左侧,属性值位于右侧。当用户调整inspector窗格的大小时,我希望属性值标签根据需要自动调整其高度

当前情况:

我希望发生的事情:

(请注意,此行为与我遇到的有关NSTextFields和自动布局的大多数堆栈溢出问题不同。这些问题希望在用户键入时文本字段增长。在这种情况下,文本是静态的,并且NSTextField被配置为像标签一样。)

更新1.0

根据@hamstergene的建议,我对NSTextField进行了子类化,并制作了一个小示例应用程序。在大多数情况下,它现在可以工作了,但现在有一个小的布局问题,我怀疑这是由于NSTextField的帧与auto layout预期的帧不完全同步造成的。在下面的屏幕截图中,右侧标签都是垂直间隔的,带有
top
约束。当窗口调整大小时,
Where
字段将正确调整大小和包装。但是,
Kind
文本字段不会被按下,直到我将窗口的大小调整为“多一个像素”

示例:如果我将窗口调整为正确的宽度,则<>代码> TeXField是第一次换行,然后在中间图像中得到结果。如果我将窗口的大小再调整一个像素,则
Kind
字段的垂直位置设置正确

我怀疑这是因为自动布局正在进行传递,然后帧被显式设置。我想auto layout在那一关上看不到,但在下一关上看到,并相应地更新位置

假设这就是问题所在,我如何将我在
setFrameSize
中所做的这些更改通知auto layout,以便它可以再次运行布局。(最重要的是,不要陷入layout-setFrameSize-layout-etc的递归状态。)

解决方案

我已经想出了一个解决方案,似乎正是我所希望的那样。我没有对
NSTextField
进行子类化,而是在所讨论的
NSTextField
superview
中重写
layout
。在
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];
      }
    }

如果检查器窗格的宽度永远不会改变,只需检查IB中的“FirstRuntime 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;
}

在这两种情况下,必须以明显的方式设置约束(您可能已经尝试过了):高垂直拥抱优先级,低水平,将所有四条边固定到superview和/或同级视图。

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

- (void)viewDidLayout {
    [super viewDidLayout];
    self.aTextField.preferredMaxLayoutWidth = self.aTextField.frame.size.width;
    [self.view layoutSubtreeIfNeeded];
}
这只是让约束系统求解宽度(此梯段上的高度将无法求解,因此将与最初设置的高度相同),然后应用该宽度作为最大布局宽度,并执行另一个基于约束的布局过程

没有子类化,没有破坏视图的布局方法,没有通知。如果不使用
NSViewController
,可以调整此解决方案,使其在大多数情况下都能工作(在自定义视图中对textfield进行子类化等)


这大部分来自于膨胀(看看多行文本节的内在内容大小)。

在节文本字段的“大小检查器”选项卡中设置“首选宽度”而不是“第一次运行时布局宽度”

这对我来说很有效,而且有点优雅。此外,我还制作了一个关于


检查器的宽度是可变的,由用户控制。它不受任何约束的控制(或定义)。只有屏幕截图中的灰色视图和NSTextField标签使用自动布局。(所有这些都是在代码中完成的,而不是在界面生成器中完成的。)我将在
layout
中重新访问子类化和手动执行layout,但我希望仅使用约束来缩小Finder的“获取信息”面板中看到的包装行为。最后,setPreferredMaxLayoutWidth是获得适当解决方案的关键
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()
      }
   }
}