Macos 在基于视图的NSTableView中,NSTextField由NSImageView剪裁

Macos 在基于视图的NSTableView中,NSTextField由NSImageView剪裁,macos,cocoa,autolayout,nstableview,nstextfield,Macos,Cocoa,Autolayout,Nstableview,Nstextfield,TL;博士: 在macOS 10.11中,一种基于视图的NSTableView,包含一个NSTextField和一个位于textfield正下方的NSImageView,其中一些行有图像,而另一些行没有图像,一些文本在向下滚动表格然后向上滚动后被剪裁 滚动前: 滚动后: 当我说“clipped”时,它意味着文本视图处于自动布局定义的最小高度,此时它应该展开。 请注意,这种错误行为只发生在MacOS10.11Mavericks中。macOS 10.12 Sierra没有此类问题 上下文 ma

TL;博士:

在macOS 10.11中,一种基于视图的NSTableView,包含一个NSTextField和一个位于textfield正下方的NSImageView,其中一些行有图像,而另一些行没有图像,一些文本在向下滚动表格然后向上滚动后被剪裁

滚动前:

滚动后:

当我说“clipped”时,它意味着文本视图处于自动布局定义的最小高度,此时它应该展开。

请注意,这种错误行为只发生在MacOS10.11Mavericks中。macOS 10.12 Sierra没有此类问题


上下文 macOS 10.11+,基于视图的NSTableView

每行有一个文本字段,文本字段下方有一个图像视图

所有图元都在IB中设置了自动布局约束

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

当然,行本身也必须调整其高度

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

这是它正常工作时的渲染方式:

当前实施 该行是NSTableCellView的子类

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

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

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(\utableview:NSTableView,viewfortablecolumn:NSTableColumn?,row:Int)->NSView?
I准备实际单元格内容:

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时,只要一行或多行在图像视图中有图像,就会出现显示问题。

剪辑文本 有时会剪裁文本,可能是因为空图像视图遮住了文本的底部:

正常的

剪短

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

我试过的 细胞再利用 我认为主要问题在于tableView对单元格的重用

因此,默认情况下,我将图像字段隐藏在
tableView(u-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仍然阻止文本视图

不管怎样,有时文本会在第一次显示时被剪切,然后再进行滚动

核心动画层 我认为可能需要对单元格进行层备份,以便正确地重新显示,因此我在scrollView、tableView、tableCell等上启用了CoreAnimation层,但我几乎看不到任何区别

A/B测试 如果我完全删除imageView,只处理文本字段,一切正常。拥有imageView肯定是这里出现问题的原因

自动布局 我曾尝试在不使用自动布局的情况下处理行内容的显示,但没有效果。我也尝试过设置不同的约束,但很明显,我不擅长自动布局,也没有找到一个好的替代当前约束的方法

备选方案:带有图像的NSAttributedString 我尝试删除图像视图,并将图像添加到文本视图中属性字符串的末尾。但是,当它工作时,结果往往是丑陋的——而且在大多数情况下,它就是不工作(例如,当图像无法及时下载并添加到属性字符串中时,使单元格完全没有文本或图像,并且高度错误)

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

或者您会完全不同地执行此操作吗?

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

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


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

您正在谈论的是
NSTextView
以及您正在使用的
NSTextField
图片和代码。您是否检查了
ns.bounds.size
heightOfRow
中是否正确?Arf,是我的错,我在测试时从NSTextView更改为NSTextField,但忘了恢复。您是否认为使用NSTextField是错误的,我应该返回NSTextView?//哇,ns.bounds.size的高度始终为零。。。不确定发生了什么,是因为NSTextField而不是NSTextView吗?我刚刚发现这在macOS 10.12中没有问题。错误的行为只发生在MacOS10.11Mavericks中。。。oo您的文本字段有一个“首选宽度”。你的IB屏幕截图显示你已经将其设置为自动。你玩过其他价值观吗?无论是在IB还是在运行时。你也玩过指数变化的RowswithIndexChanged的NoteHeightofRowswithIndexChanged。自动布局可能需要再次调用您的代理。有些人称之为自动布局魔术。其他人则将其视为自动布局戏剧。这里真的没有什么可补充的。我的应用程序中有几个地方出现了类似的问题,它解决了这个问题。我认为这与auto layout计算约束的顺序以及文本字段具有固有大小的事实有关。这解决了Mavericks中的问题,并且似乎不会改变Sierra中的任何内容。非常感谢,尤金!:)