自定义文本视图的自动布局支持
Posted
技术标签:
【中文标题】自定义文本视图的自动布局支持【英文标题】:Autolayout support for custom text view 【发布时间】:2019-03-04 21:52:24 【问题描述】:这个问题是关于使用约束和视图的intrinsicContentSize 来支持可变高度的自定义文本视图以实现自动布局。在您点击“复制”按钮之前,请听我说完。
我有一个自定义文本视图(从头开始,继承自 NSView)。它支持许多常见的 NSTextView 功能,这里最相关的是多行并根据可用宽度布置其内容。它构建的应用程序将这些文本视图中的几个加载到表格视图的每一行中。问题是没有根据它的 intrinsicContentSize 正确设置高度。
我创建了一个sample project 来简化问题。它使用具有固定数量和大小的字符的“伪”文本视图,用于确定所需的宽度/高度。在示例中,有一列的表格视图,其单元格视图只有一个子视图,即 PseudoTextView。文本视图被固定在其单元格视图的边缘,并带有一点填充。如何让系统识别文本视图应该遵守定义宽度的约束,同时允许文本视图在高度上增长,并被单元格视图紧紧包裹?这是文本视图代码:
class PseudoTextView: NSView
@IBInspectable var characterCount: Int = 0
@IBInspectable var characterWidth: CGFloat = 5
@IBInspectable var characterHeight: CGFloat = 8
@IBInspectable var background: NSColor = .blue
didSet
layer?.backgroundColor = background.cgColor
required init?(coder decoder: NSCoder)
super.init(coder: decoder)
wantsLayer = true
layer?.backgroundColor = background.cgColor
override var intrinsicContentSize: NSSize
let requiredWidth = characterWidth * CGFloat(characterCount)
let lineCount = (requiredWidth / frame.width).rounded(.up)
let usedHeight = lineCount * characterHeight
let charactersPerLine = (frame.width / characterWidth).rounded(.down)
let usedWidth = charactersPerLine * characterWidth
return NSSize(width: usedWidth, height: usedHeight)
此版本根据视图的框架返回适当的大小。这显然不起作用,因为它是在布局的 updateConstraints 阶段在尚未设置框架时访问的。我也尝试过使用 NSView.noIntrinsicMetric 作为宽度,但这会将文本视图驱动为零宽度并且高度永远不会恢复。我还做了很多其他尝试,但我不会全部让你厌烦。
NSTextField 做了一些不同的事情(假设'Editable' 关闭,'Wrap' 开启)。它的 intrinsicContentSize 在一行上报告文本的全宽(即使它比可用宽度长得多),但它以某种方式调整为正确的宽度。调整大小后,intrinsicContentWidth 仍会报告完整的单行宽度,但会调整高度以考虑多行。在我无法预测的地方有一些魔法。
我已经阅读了相关文档的每一行。如果有关于该主题的博客文章,我可能已经阅读过。如果关于这个话题有关于 SO 的问题,我可能已经读过了。如果你写了一本关于这个主题的书,我可能已经买了。所有这些消息来源都在取笑我遇到的问题,但没有一个能回答如何处理这种特殊情况的问题。绝望。
更新:
在阅读了 Jonathon Mah (http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html) 的一篇旧博文后,我创建了一个使用他的方法的示例。这是another project,它模仿了他的技术并正常工作。这是应用程序的顶部。这是一个使用滑块调整的固定容器视图。拼凑是自定义视图的伪字符,背景为粉红色。
但是,当插入到自调整大小的表格视图中时,自定义视图正确匹配其单元格的宽度,但未调整单元格以尊重固有高度。如果您将自定义视图的底部约束更改为可选的(例如,使用 >= 关系),自定义视图会缩小到正确的高度,但单元格视图保持固定。如何说服单元格视图缩小其高度以尊重其子视图的 intrinsicContentSize.height?
【问题讨论】:
嗯示例项目没有说明问题。输入Entry
结构的 int 似乎正确地改变了单元格的大小。
它确实有效果,但它与应有的单元格宽度和intrinsicHeight 不匹配。我将用更多细节和另一个示例更新我的问题。
【参考方案1】:
我对您的问题有一个解决方案,尽管它可能不是最佳的,因为我对 macos 细节没有太多经验。所以,首先,让我们定义表格视图应该使用自动行高:
override func awakeFromNib()
super.awakeFromNib()
tableView.usesAutomaticRowHeights = true
在您的第二个示例中,tableView 插座没有连接到TableViewController
,但它可能应该连接,所以不要忘记连接它。
现在,在您的WrappingCellView
中,您覆盖了layout()
,但您为preferredMaxLayoutWidth
设置的值不正确。我觉得应该是superview的宽度:
override func layout()
// 16 is used for the sake of example
wrappingView.preferredMaxLayoutWidth = (superview?.frame.width ?? 16) - 16
super.layout()
下一部分是我不确定的部分:
func tableViewColumnDidResize(_ notification: Notification)
tableView.reloadData()
应该有更好的 API 来重新计算行高。我希望你或其他人可以提出一些建议:)
这三个调整会导致正确重新计算单元格高度:
【讨论】:
可能有几种方法可以解决这个问题,你的方法看起来不错。感谢您指出没有拨动正确的开关和连接插座的愚蠢错误——我已经将几个项目放在一起试图解决这个问题,但并不总是很小心。多亏了 Jonathon Mah 离线的一些慷慨指导,我有了一个更简单的解决方案。表格视图加载自己的约束,称为“封装布局宽度/高度”,优先级为 500。调整水平压缩阻力 500 就可以了。非常感谢您的帮助!为你的努力点赞以上是关于自定义文本视图的自动布局支持的主要内容,如果未能解决你的问题,请参考以下文章