I'm using an NSTableView and DifferenceKit. This is all programmatic, no Interface Builder at all.
I'd previously implemented only tableView(_:objectValueFor:row)
in order to get values into my table. At that point I could apply full and partial reloads and everything worked fine.
Now I've added an implementation of tableView(_:viewFor:row:)
in order to format some columns differently, and it's affected reloading. A full reloadData()
still works, but a call to reloadData(forRowIndexes:columnIndexes)
doesn't call either my datasource or delegate methods; the reload seems to simply disappear.
I also tried removing the datasource method and running only with tableView(_:viewFor:row:)
but no dice. A partial reload still doesn't call the delegate method.
Has anyone come across this? Is there a nuance of NSTableView I'm missing?
My code (truncated):
init() {
...
tableView.dataSource = self
tableView.delegate = self
columns.forEach {
tableView.addTableColumn($0)
}
}
func tableView(
_ tableView: NSTableView,
objectValueFor tableColumn: NSTableColumn?,
row: Int
) -> Any? {
...
return someStringProvider(column, row)
}
func tableView(
_ tableView: NSTableView,
viewFor tableColumn: NSTableColumn?,
row: Int
) -> NSView? {
...
if let existingView = tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView {
existingView.textField?.stringValue = someStringProvider(column, row)
return existingView
}
let textField = NSTextField()
...
textField.stringValue = someStringProvider(column, row)
let view = NSTableCellView()
view.identifier = identifier
view.addSubview(textField)
view.textField = textField
view.addConstraints([
... (pin textField to view)
])
textField.bind(
.value,
to: view,
withKeyPath: "objectValue",
options: nil
)
return view
}
This turns out to be either a misunderstanding of, or possibly a bug in, the way DifferenceKit interacts with a view-based, rather than cell-based, NSTableView.
I was crucially not checking the values with which reloadData(forRowIndexes:columnIndexes)
was called, and the columnIndexes passed by DifferenceKit's algorithm were always [0]
, which was not valid for any visible cells as my first column is hidden, so the methods weren't called.
The useful lesson here is that when a tableview's delegate lacks tableView(_:viewFor:row:)
, a reload to any column reloads the entire row, but once views are specified, the column values become relevant.