首页 > 解决方案 > 自定义文本视图的自动布局支持

问题描述

这个问题是关于使用约束和视图的intrinsicContentSize 来支持可变高度的自定义文本视图以进行自动布局。在您单击“复制”按钮之前,请听我说完。

我有一个自定义文本视图(从头开始,继承自 NSView)。它支持许多常见的 NSTextView 功能,这里最相关的是多行并根据可用宽度布置其内容。它构建的应用程序将这些文本视图中的几个加载到表格视图的每一行中。问题是没有根据它的内在内容大小正确设置高度。

我创建了一个简化问题的示例项目。它使用具有固定数量和大小的字符的“伪”文本视图,用于确定所需的宽度/高度。在示例中,有一列的表格视图,其单元格视图只有一个子视图,即 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 做了一些不同的事情(假设“可编辑”关闭,“包装”打开)。它的 intrinsicContentSize 在一行上报告文本的全宽(即使它比可用宽度长得多),但它以某种方式调整为正确的宽度。调整大小后,intrinsicContentWidth 仍会报告完整的单行宽度,但会调整高度以适应多行。在我无法预测的地方有一些魔法。

我已经阅读了相关文档的每一行。如果有关于该主题的博客文章,我可能已经阅读过。如果关于这个话题有关于 SO 的问题,我可能已经读过了。如果你写了一本关于这个主题的书,我可能已经买了。所有这些消息来源都在取笑我遇到的问题,但没有一个能回答如何处理这种特殊情况的问题。绝望的。

更新:

在阅读了 Jonathon Mah 的一篇旧博客文章 ( http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html ) 之后,我创建了一个使用他的方法的示例。这是另一个模仿他的技术并正常工作的项目。这是应用程序的顶部。这是一个使用滑块调整的固定容器视图。拼凑是自定义视图的伪字符,其背景是粉红色。

在此处输入图像描述

但是,当插入到自调整大小的表格视图中时,自定义视图会正确匹配其单元格的宽度,但不会调整单元格以尊重固有高度。如果您将自定义视图的底部约束更改为可选的(例如,使用 >= 关系),自定义视图会缩小到正确的高度,但单元格视图保持固定。如何说服单元格视图缩小其高度以尊重其子视图的intrinsicContentSize.height?

在此处输入图像描述

标签: macosautolayoutnstextviewintrinsic-content-size

解决方案


我有一个解决您的问题的方法,尽管它可能不是最佳的,因为我对 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 来重新计算行高。我希望你或其他人可以提出一些建议:)

这三个调整会导致正确重新计算像元高度: 在此处输入图像描述


推荐阅读