1

I've seen at least one StackOverflow answer suggesting that I need to use a NSTableRowView if I want a custom color for my NSTableView's selected cell state. So I created a custom subclass and added some NSTableViewDelegate calls to use the subclass:

   func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
        return QueriesTableRow()
    }
    
    override func didAdd(_ rowView: NSTableRowView, forRow row: Int) {
        rowView.backgroundColor = QueriesTableRow.baseColor
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
        if let tableView = notification.object as? NSTableView {
            tableView.enumerateAvailableRowViews { (rowView, _) in
                print("Calling setNeedsDisplay on: \(rowView)")
                rowView.setNeedsDisplay(rowView.bounds)
            }
        }
    }

And here's the code for the NSTableRowView subclass:

class QueriesTableRow: NSTableRowView {
    static let baseColor = NSColor.yellow.withAlphaComponent(0.4)
    
    override func drawSelection(in dirtyRect: NSRect) {
        var color: NSColor
        if isSelected {
            color = NSColor.tertiaryLabelColor
        } else {
            color = QueriesTableRow.baseColor
        }
        print("QueriesTableRow set color to: \(color) on \(self)")
        backgroundColor = color
    }    
}

This works fine to display the cell as desired initially, and to change its color when it's selected. But when I deselect a selected cell, calling setNeedsDisplay on the rowView does not trigger its drawSelection(in:) method, and the cell's color does not revert to reflect its deselected state.

The print statements produce:

QueriesTableRow set color to: Catalog color: System tertiaryLabelColor on <Analytics_Query.QueriesTableRow: 0x7fa4d8c26610> - row: 0

Calling setNeedsDisplay on: <Analytics_Query.QueriesTableRow: 0x7fa4d8c26610> - row: 0

QueriesTableRow set color to: Catalog color: System tertiaryLabelColor on <Analytics_Query.QueriesTableRow: 0x7fa4d8c26610> - row: 0

Calling setNeedsDisplay on: <Analytics_Query.QueriesTableRow: 0x7fa4d8c26610> - row: 0

Can somebody suggest a fix for this?

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
Cosmo
  • 171
  • 1
  • 8
  • Does this answer your question? [Change selection color on view-based NSTableView](https://stackoverflow.com/questions/9463871/change-selection-color-on-view-based-nstableview) – Willeke Apr 18 '21 at 09:52
  • It gets me closer to an acceptable solution, but not exactly what I wanted. I wanted to be able to define the cell color for selected or unselected state. This allows me to add a grey tinge over the cell's defined color. Not ideal but better than nothing. The problem still appears to be that the drawSelection(in: ) method is not called when the cell's selected state goes from true to false. – Cosmo Apr 18 '21 at 17:11

1 Answers1

1

After watching the WWDC 2011 video referenced in the answer that Willeke pointed me to, I came up with a solution that does exactly what I want.

Here's the NSTableViewDelegate code involved:

    func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
        return QueriesTableRow()
    }
    
    override func didAdd(_ rowView: NSTableRowView, forRow row: Int) {
        rowView.backgroundColor = QueriesTableRow.baseColor
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
        if let tableView = notification.object as? NSTableView {
            tableView.enumerateAvailableRowViews { (rowView, _) in
                if rowView.isSelected {
                    rowView.backgroundColor = NSColor.tertiaryLabelColor
                } else {
                    rowView.backgroundColor = QueriesTableRow.baseColor
                }
                rowView.displayIfNeeded()
            }
        }
    }

And here's the custom NSTableRowView code:

class QueriesTableRow: NSTableRowView {
    static let baseColor = NSColor.yellow.withAlphaComponent(0.4)
    
    override func drawSelection(in dirtyRect: NSRect) {
        //
    }
}

Now my cells display the specific color I want for selected and deselected state.

Cosmo
  • 171
  • 1
  • 8