我有一个NSSplitView(包含两个窗格)填充窗口的程序。左窗格是NSStackView,它包含三个nsbox。前两个字段都包含一个NSTextField,用于显示一系列天气信息,这些信息可以在每次由后台代码更新时更改长度。我已经在前两个nsbox(及其子NSTextFields)中添加了相关的约束,使它们随着文本长度的变化而展开或缩小,以适应其中文本字段的大小。第三个NSBox将填充堆栈视图中的剩余空间。
问题是,当窗口加载时,nsbox(如预期的那样)会显示在interface builder中设计的正确大小,以适合文本字段中的默认字符串。但是,当更新代码启动并将文本字段的文本更改为下载的数据(比默认字符串长)时,nsbox不会调整其高度以适应较大的文本。它们只在拆分视图的拆分器移动时更改高度。这很烦人,因为每当文本字段中的行数更改时,我都必须移动拆分器。为什么会发生这种情况?当文本比以前长或短时,如何使框更新高度以适合文本?
这很像这个问题(我找到的关于我的问题的唯一信息来源):NSScrollView with auto layout not resizing until first manual window resize,但是解决方案对我不起作用。
下面是界面的图像(同样,顶部的两个框应该调整大小以适合其文本,但这只在拆分器移动时发生)。它显示的文本比框所能显示的要长,但它们没有调整大小:
最佳答案
一个选项是创建NSBox
的子类并重写intrinsicContentSize
属性。在您的实现中,您将测量框的文本字段中包含的文本的高度,考虑文本字段的垂直边缘和框的相应边缘之间存在的任何垂直填充,并将计算结果作为height
部分的NSSize
返回值返回。然后,只要想调整框的大小,就可以调用invalidateIntrinsicContentSize
。
我创建了一个sample app来查看我是否可以让这个工作,我已经在这里发布了。有几个棘手的部分你应该注意:
计算文本高度
这种方法中最复杂的编码位是计算文本的高度。幸运的是,苹果有documentation告诉你如何做到这一点;下面是Swift中的情况:
// Properties of your NSBox subclass
var layoutManager: NSLayoutManager {
return textStorage.layoutManagers.first! as! NSLayoutManager
}
var textContainer: NSTextContainer {
return layoutManager.textContainers.first! as! NSTextContainer
}
var typesetter: NSTypesetter {
return layoutManager.typesetter
}
// The textStorage object lies at the heart of the text stack. Create this
// object first, the rest follows.
lazy var textStorage: NSTextStorage! = {
var initialString: String = ""
var ts = NSTextStorage(attributedString: self.textField.attributedStringValue)
var lm = NSLayoutManager()
lm.typesetterBehavior = NSTypesetterBehavior.Behavior_10_2_WithCompatibility
var tc = NSTextContainer()
lm.addTextContainer(tc)
ts.addLayoutManager(lm)
return ts
}()
为您的
NSBox
子类设置拥抱抵抗优先级为了让演示正常工作,我发现需要将
NSBox
对象的垂直拥抱优先级和垂直压缩阻力值设置为900。