macos - 自定义 TextView 的自动布局支持

标签 macos autolayout nstextview intrinsic-content-size

这个问题是关于使用约束和 View 的内在内容大小来支持可变高度的自定义 TextView 以进行自动布局。在您点击“复制”按钮之前,请先听我说完。

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

我创建了一个sample project这简化了问题。它使用“伪” TextView ,具有固定数量和大小的字符,用于确定所需的宽度/高度。在示例中,有一个一列的表格 View ,其单元格 View 只有一个 subview ,即 PseudoTextView。 TextView 通过一点填充固定到其单元格 View 的边缘。如何让系统认识到 TextView 应该遵守定义宽度的约束,同时允许 TextView 的高度增长,并被单元格 View 紧紧包裹?这是 TextView 代码:

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)
}

此版本根据 View 的框架返回适当的大小。这显然不起作用,因为它是在布局的 updateConstraints 阶段(当框架尚未设置时)访问的。我还尝试使用 NSView.noIntrinsicMetric 作为宽度,但这会将 TextView 驱动到零宽度,并且高度永远不会恢复。我还进行了大量其他尝试,但我不会一一列举让您感到厌烦。

NSTextField 做了不同的事情(假设“Editable”关闭,“Wrap”打开)。它的intrinsicContentSize 报告单行上文本的完整宽度(即使它比可用宽度长得多),但它会以某种方式调整为正确的宽度。调整大小后,intrinsicContentWidth 仍会报告完整的单行宽度,但会调整高度以考虑多行。有一些我无法预见的魔法。

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

更新:

阅读 Jonathon Mah (http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html) 的旧博客文章后,我创建了一个使用他的方法的示例。这是another project模仿他的技术并且工作正常。这是应用程序的顶部部分。它是一个固定的容器 View ,可以使用 slider 进行调整。拼凑而成的是自定义 View 的伪字符,其背景是粉红色。

enter image description here

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

enter image description here

最佳答案

我对你的问题有一个解决方案,尽管它可能不是最佳的,因为我对 macOS 细节没有太多经验。因此,首先,我们定义 TableView 应使用自动行高:

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 来重新计算行高。我希望你或其他人可以提出一些建议:)

这三项调整会导致单元高度的正确重新计算: enter image description here

关于macos - 自定义 TextView 的自动布局支持,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54992110/

相关文章:

swift - 使用外部源动态创建 NSPopUpButton (项目数量)

Snow Leopard Xcode 中的 C++ 调试构建中断

ruby - 在没有Xcode命令行工具的情况下安装Ruby时出错: No such file, but it exists

objective-c - 在 NSTableView 中添加页脚

ios - Autolayout 在旋转时改变了 UIScrollView 的 contentOffset

cocoa - NSTextView高亮显示

objective-c - 使用 NSString 变量设置 NSTextView 的文本,考虑引用计数

cocoa - NSTextField 和 NSTextView : Overlapping Delegate Methods

ios - 边距空间 UITableView (Swift)

ios - 具有多个按钮和字体的多行 UILabel