在基于视图的NSTableView中,NSTextField被NSImageView

时间:2017-01-16 11:16:46

标签: macos cocoa autolayout nstableview nstextfield

TL; DR:

在macOS 10.11中,基于视图的NSTableView在文本字段下方包含NSTextField和NSImageView,其中一些行具有图像而其他行没有,有些文本在向下滚动表格之后被剪切。

滚动前:

enter image description here

滚动后

enter image description here

当我说"剪辑"这意味着文本视图处于自动布局定义的最小高度,而应该扩展它。

请注意,此错误行为仅发生在macOS 10.11 Mavericks中。 macOS 10.12 Sierra没有这样的问题。

上下文

macOS 10.11 +,基于视图 NSTableView。

每行都有一个文本字段,以及文本字段正下方的图像视图。

所有元素都具有在IB中设置的自动布局约束。

enter image description here

enter image description here

目标

文本视图必须垂直调整其高度 - 图像视图应相应移动,保持粘在文本字段的底部。

当然行本身也必须适应它的高度。

有时候没有要显示的图像,在这种情况下,图像视图不应该是可见的。

这是它在正常工作时应该呈现的方式:

enter image description here

当前实施

该行是NSTableCellView的子类。

在单元格中,文本字段设置为属性字符串。

tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat中,我创建了一个虚拟textView,用于查找实际文本字段的高度,并相应地返回修改后的行高。我还检查是否有要显示的图像,如果是这样的话我将图像高度添加到行高。

let ns = NSTextField(frame: NSRect(x: 0, y: 0, width: defaultWidth, height: 0))
ns.attributedStringValue = // the attributed string
var h = ns.attributedStringValue.boundingRect(with: ns.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading]).height
if post.hasImage {
    h += image.height
}
return h + margins

tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?中,我准备了实际的细胞内容:

getUserAvatar { img in
    DispatchQueue.main.async {
        cell.iconBackground.layer?.backgroundColor = self.prefs.colors.iconBackground.cgColor
        cell.iconBackground.rounded(amount: 6)
        cell.iconView.rounded(amount: 6)
        cell.iconView.image = img
    }    
}

cell.usernameLabel.attributedStringValue = xxx
cell.dateLabel.attributedStringValue = xxx

// the textView at the top of the cell
cell.textLabel.attributedStringValue = xxx

// the imageView right under the textView
if post.hasImage {
    getImage { img in
        DispatchQueue.main.async {
            cell.postImage.image = img
        }
    }
}

问题

滚动tableView时,只要一行或多行在图像视图中显示图像,就会出现问题。

剪辑文字

有时文字被剪裁,可能是因为空图像视图掩盖了文本的底部:

正常

enter image description here

削波

enter image description here

重要提示:调整表格大小会触发重绘并修复显示问题......

我尝试了什么

细胞再利用

我认为主要问题是因为tableView重用了单元格。

所以我默认隐藏了tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?中的图片字段,只有在设置图像时才取消隐藏,我在主线程上执行此操作:

guard let cell = tableView.make(withIdentifier: "xxx", owner: self) as? PostTableCellView else {
    return nil
}
DispatchQueue.main.async {
    cell.postImage.isHidden = true
    cell.postImage.image = nil
}
// set the text view here, the buttons, labels, etc, then this async part runs:
downloadImage { img in
    DispatchQueue.main.async {
        cell.postImage.isHidden = false
        cell.postImage.image = img
    }   
}
// more setup
return cell

但在某些情况下,imageView仍然会阻止文本视图。

无论如何,有时在完成任何滚动之前,文本会在第一个显示处被剪切......

CoreAnimation Layer

我想也许这些单元格需要进行图层备份才能正确地重新显示,所以我在scrollView,tableView,tableCell等上启用了CoreAnimation图层。但我几乎看不出任何区别

A / B测试

如果我完全删除imageView并且只处理文本字段,一切正常。拥有imageView绝对是导致问题的原因。

自动布局

我试图在不使用自动布局的情况下处理行内容的显示,但无济于事。我也试图设置不同的约束条件,但显然我对autolayout感到厌烦,并没有设法找到一个替代当前约束的好方法。

替代方案:NSAttributedString with image

我尝试删除图片视图,并在文本视图中的属性字符串末尾添加图片。但结果往往是丑陋的 - 当它工作时 - 并且大多数时候它只是不起作用(例如,当图像无法及时下载以添加到属性字符串时,使单元格没有文本或图像完全和错误的高度。)

问题

我做错了什么?我该如何解决这些问题?我的猜测是我应该更改自动布局限制,但我没有看到有效的解决方案。

或许你会以完全不同的方式做到这一点?

1 个答案:

答案 0 :(得分:1)

文本字段在IB中具有“首选宽度”字段,允许调整内在大小与自动布局计算大小的相关性。

将此IB属性值更改为“First Runtime Layout Width”或“Explicit”通常有助于解决类似问题。

enter image description here

这解决了我过去的很多问题。