NSTableView - How can the background color of the row views be changed dynamically?

Context:


View based NSTableView - OS X 10.10 and later.


Problem:


I want to change the background color of the displayed rows in a table view. Basically, I have a button in the window and when I click this button I want to change the background color of some rows.


Well, this does not work.


Things I've tried:


1. Implementing tableView:didAddRowView:forRow: and tableView:didRemoveRowView:forRow:


This does not work as these methods are not invoked when calling reloadData or reloadDataForRowIndexes:columnIndexes: .The change is only applied when a new row is added.


And trying to lie with noteNumberOfRowsChanged does not help.


2. Implementing tableView:rowViewForRow:


This does not work, the background color set in this method was never taken into account when I tried this.


Question:


Am I missing something an obvious solution?

Have you tried simply obtaining a reference to the row view(s) using -rowViewAtRow:makeIfNecessary: and then setting its(their) backgroundColor property?

You would have code like this:


in


    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

                if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), owner: self) {

                    if row == selected { 
                        (cellView as! NSTableCellView).backgroundColor = .red // If you want red background

Yes, I tried this in the tableView:viewForTableColumn:row: method (as the color of the row can change and row views can be reused).


This does not work either.

There are no backgroundColor properties on NSTableCellView. There is one for layers. But I don't enable layers.


Also this does not fill the entire row (width or height).

I believe I found the issue:


reloadData was actually not called because Foundation is both quite clever and completely dumb when it comes to empty NSSet or NSArray instances.


2 calls to [NSSet setWithArray:@[]]; will return the same "pointer".


So if you try to compare the 2 instances in a setter method, they will be equal even from an address point of view.


The tableView:didAddRowView:forRow: and tableView:didRemoveRowView:forRow: works when the problem above is addressed.

NSTableRowView has a property backgroundColor, but this is also used by AppKit when you set tableView.usesAlternatingRowBackgroundColors = true. The only way I could make it work was by returning a custom row view subclass and dynamically returning a custom background color.

class MyRowView: NSTableRowView {
    
    var myBackgroundColor: NSColor?
    
    override var backgroundColor: NSColor {
        get {
            return myBackgroundColor ?? super.backgroundColor
        }
        set {
            super.backgroundColor = newValue
        }
    }
}

and in the delegate

func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
    if myCondition {
        (rowView as! MyRowView).myBackgroundColor = myColor
    }
}
NSTableView - How can the background color of the row views be changed dynamically?
 
 
Q