如何去大约有自动布局自动包装一个的NSTextField多行作为的变化的NSTextField的宽度?
我有许多NSTextFields显示在检查窗格静态文本(即:标签)。当检查员窗格被用户调整,我想右侧标签回流到多条线路如果需要的话。
(Finder的获取信息面板做到这一点。)
但我一直无法弄清楚的自动布局约束的组合,以允许这种行为。在所有的情况下,在右边的NSTextFields拒绝包裹。 (除非我明确地添加一个高度约束,将允许它。)
视图层次结构是这样的,每一个灰色带是含有两个NSTextFields,左侧的属性名称和右边的属性值的图。当用户调整检查窗格,我想属性值标签自动调整它的高度需求,是。
现在的情况:
我想有发生:
(请注意,这种行为比大多数堆栈溢出的问题不同,我碰到关于NSTextFields和经销商的布局。这些问题要在文本字段增长,而用户键入。在这种情况下,该文本是静态的和的NSTextField配置看就像一个标签。)
1.0更新
以@ hamstergene的建议下,我子类的NSTextField,并提出了小样本应用程序。在大多数情况下,它现在的作品,但现在有,我怀疑是不是在完全同步作为的NSTextField的帧的结果什么自动布局希望它是一个小的布局问题。在下面的屏幕截图中,右手侧的标签都被垂直地一个top
约束隔开。随着窗口的大小时,Where
场得到适当调整和包裹。然而,Kind
文本字段不被推下来,直到我调整窗口“多了一个像素”。
例如:如果我调整窗口的大小,以恰到好处的宽度,该文本框Where
做它的第一个包,然后我得到的中间图像中的结果。如果我调整窗口,多了一个像素,则该Kind
领域的垂直位置设置正确。
我怀疑这是因为自动布局是这样做的传球,然后框架越来越明确设置。我想象自动布局没有看到对通,但确实是它的下传,并相应地更新位置。
假设这是个问题,我怎么告诉这些改变我做的setFrameSize
的自动布局,以便它可以再次运行布局。 (而且,最重要的是,不会被抓到在递归状态布局setFrameSize布局,等...)
解
我想出了,似乎我的工作究竟是如何希望的解决方案。相反子类NSTextField
的,我只是覆盖layout
在有关superview
的NSTextField
。在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(注意这是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;
}
在这两种情况下,约束必须设置明显的方式(你可能已经尝试过):高垂直拥抱优先级低的水平,销全部四个边缘,上海华盈和/或姊妹的意见。
最简单的方法来得到这个工作,假设你使用的是基于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(看多行文本部分的内在内容大小)。
设置在尺寸检查标签在部分文本字段首选宽度设置为“首先运行版式宽度”
这个工作对我来说,是有点更优雅。此外,我所做的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()
}
}
}