How can I update the datasource presented in a programmatically created NSTableView

66 Views Asked by At

I am new to Cocoa (and Swift) and am trying to wrap my head around a (View based) NSTableView, NSTableViewDataSource, NSTableViewDelegate, Storyboards, Notifications, etc. I am trying to create an NSTableView with a varying amount of columns (and rows). My header is a [String], my rows are a [[String]] (though if it's simpler for identification purposes, it could also be an [[String: String]]). There's a lot of tutorials/SO questions out there covering similar topics, so generating the table programmatically has not been an issue. However, I cannot figure out how to get notified to store data back into my [[String]] once a user edits a cell.

Please realise that I have probably looked at every post covering this topic on the Apple forums, SO, and others. If marking this question as a duplicate, please tell me which part of your link I have to look at and how I can adapt it for my purposes.

Currently my code is pretty much Robert's answer on this SO question with a couple of buttons added: one to print() my [[String]] datasource, and another to call his setContent() function. I have also added cell.textField?.isEditable = true to tableView(_:viewFor:row:) to make the cells editable.

I have found some answers pointing to tableView(_:setObjectValue:for:row:), however that never seems to get called - I assume because as per Apple's docs that is meant for Cell based NSTableViews, not View based NSTableViews. That page says "In view-based tables, use target/action to set each item in the view cell.", but does not go into further detail on how to implement this.

This seems very close but it's in Obj-C and I find difficult to adapt for Swift.

1

There are 1 best solutions below

2
Cherub On

I ended up changing the tableView(_:viewFor:row:) function to include cell.textField?.target = self and cell.textField?.action = #selector(self.onEditCell(_:)), like so:

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
    tableColumn?.headerCell.title = header[column];

    if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self) as? NSTableCellView {
        cell.textField?.stringValue = list[row][column]
        cell.textField?.isEditable = true
        cell.textField?.target = self                           // <-- added
        cell.textField?.action = #selector(self.onEditCell(_:)) // <-- added
        return cell
    }

    return nil
}

and adding the function:

@IBAction func onEditCell(_ sender: Any) {
    let row: Int = tableView.row(for: sender as! NSView)
    let col: Int = tableView.column(for: sender as! NSView)
    let content: String = (sender as? NSTextField)?.stringValue ?? ""

    print("Col: \(col), row: \(row), value: \(content)")
}