NSTableView reloadData(forRowIndexes:columnIndexes:) 会中断自动布局



我有一个NSTableView,可以根据行的数据值交换不同的单元格视图。当模型更改时,我重新加载表,表的委托将为新数据提供正确的表单元格视图。

该表对其单元格视图使用自动布局。所有单元格视图最初都会正常加载。在模型更改后更新表时,根据我调用reloadData()还是reloadData(forRowIndexes:columnIndexes),我得到不同的结果。使用reloadData()时,将加载单元格视图,并且自动布局工作正常。如果我使用reloadData(forRowIndexes:columnIndexes),自动布局会产生完全不同的、意想不到的结果。

我创建了一个示例项目来演示该问题。 下面是项目设置的图像,包括在表单元格视图上设置的约束。有两个行模板,一个具有蓝色视图(偶数行(,另一个具有绿色(奇数行(,应跨越表格宽度(减去一点填充(。控制器提供单元视图:

class TableController: NSObject {
@IBOutlet weak var tableView: NSTableView!
var colorData = [1, 0, 1, 0]
@IBAction func swapLine(_ sender: Any) {
colorData[1] = (colorData[1] + 1) % 2
//        tableView.reloadData()
tableView.reloadData(forRowIndexes: [1], columnIndexes: [0])
}
}
extension TableController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return colorData.count
}
}
extension TableController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cellId = (colorData[row]) % 2 == 0 ? "EvenCell" : "OddCell"
return tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(cellId), owner: self)
}
}

界面中的按钮只是将数据交换为第 1 行并重新加载数据。初始视图如下所示(交替的绿色和蓝色矩形(。如果使用reloadData(),它看起来像这样(第 1 行从蓝色变为绿色(。但是,如果使用reloadData(withRowIndexes:columnIndexes:),单元格视图将缩小到 40 磅宽的 vice 480,就像在其他视图中一样。这是视图调试器的抓取,显示大小错误的单元格视图并显示不明确的宽度约束(使用reloadData()时不会发生这种情况(。

文档提到行视图与reloadData(forRowIndexes:columnIndexes:)重用,但不与reloadData()重用,我已经验证过。我想行视图的这种重用是导致自动布局问题的原因,但我找不到任何联系。在SO,AppKit发行说明,WWDC视频,Google搜索或将我的头撞在桌子上时找不到任何东西。将不胜感激。

更新:这是ColorView的代码:

class ColorView: NSView {
@IBInspectable var intrinsicHeight: CGFloat = 20
@IBInspectable var color: NSColor = NSColor.blue
override var intrinsicContentSize: NSSize {
return NSSize(width: NSView.noIntrinsicMetric, height: intrinsicHeight)
}
override func draw(_ dirtyRect: NSRect) {
color.setFill()
dirtyRect.fill()
}
}

我想我已经让它工作了。如果我在单元格返回之前对单元格调用layoutSubtreeIfNeeded()(以便已经设置了它的所有子视图(如动态文本(,那么它似乎可以工作。

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
//...
cell.layoutSubtreeIfNeeded()
return cell
}

我希望这有所帮助。

我遇到了同样的问题,并注意到需要reloadData的行缺少实际的自动布局约束。我的(黑客(解决方案是添加应该手动为单元格自动设置的约束。请注意,在我的表视图中,我只使用一列,因此我可以将宽度约束设置为等于行的宽度,而不是依赖于指定的列宽度。

class CustomRowView: NSTableRowView {
override func addSubview(_ view: NSView) {
super.addSubview(view)
// Add constraints NSTableView is supposed to set up
view.topAnchor.constraint(equalTo: topAnchor).isActive = true
view.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0).isActive = true
view.layoutSubtreeIfNeeded()
}
}

最新更新