xcodeswift5nstableviewnstextfieldcell

How to colour individual rows in Cell-Based NSTableVew in Swift 5


I am trying to show items in NSTableView but one of them (the item that previously was activated by an action (its name is stored in alreadyActivatedItem variable)) should be disabled and shown with a red text.

So far I managed to make disabling work properly.

I just cannot manage colouring the already activated item be red text. My code below will colour ALL cells' text in red.

extension PreferencesViewController: NSTableViewDelegate {
    
    // disable selecting the already activated item
    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
                        
        return !(myArray[row].name == alreadyActivatedItem)
    }
    
    // colouring the already activated item in red (it is also disabled)
    func tableView(_ tableView: NSTableView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, row: Int) {
        
        guard let c = cell as? NSTextFieldCell else {
            return
        }

        if c.stringValue == alreadyActivatedItem {
            c.textColor = .red
        }

    }
}

I also tried an other way:

// colouring the already activated item in red (it is also disabled)
func tableView(_ tableView: NSTableView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, row: Int) {

    guard let c = tableColumn?.dataCell(forRow: row) as? NSTextFieldCell else {
        return
    }
    
    if c.stringValue == alreadyActivatedRow {
        c.textColor = .red
    }
}

In both cases I will have all the rows with red text:

see as all items are red text

While debugging, I can see that:

So why still do all the items get red colour?

How to achieve my goal then?

(Xcode 11.3.1, Swift 5.1.3)


Solution

  • Cells are reused. You have to add an else clause to set the color always to a defined state.

    if c.stringValue == alreadyActivatedItem {
        c.textColor = .red
    } else {
        c.textColor = .black
    }
    

    Or simpler

     c.textColor = c.stringValue == alreadyActivatedItem ? .red : .black
    

    I recommend even a view based table view and Cocoa Bindings.