5

I am working on building MacOS app. I am trying to make table view that updates the cell when I press add button.

enter image description here

Following is my code:

 func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let identifier = tableColumn?.identifier as NSString?
    if ( identifier == "NameCell")
    {
        var result: NSTableCellView
        let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView
        cell.textField?.stringValue = self.data[row].setting!
            return cell

    }
    else if (identifier == "SettingCell")
    {
        if let cell = tableView.make(withIdentifier: "SettingCell", owner: self) as? NSTableCellView {
        cell.textField?.stringValue = self.data[row].setting!
        return cell
    }
    }
    return nil
}

However, the line let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView is keep failing because it returns nil

fatal error: unexpectedly found nil while unwrapping an Optional value

NameCell is from enter image description here Can anyone please help me find a way to solve this problem?

Marek H
  • 5,173
  • 3
  • 31
  • 42
user3284302
  • 129
  • 1
  • 8

2 Answers2

11

For anyone else who comes here with this same question when trying to make an NSTableView fully programmatically: makeView(withIdentifier:owner:) WILL return nil unless a corresponding NIB exists for the given identifier:

NSTableView documentation:

If a view with the specified identifier can’t be instantiated from the nib file or found in the reuse queue, this method returns nil.

Likewise, the 'owner' param is a NIB-specific concept. In short: you cannot use this method if populating your NSTableView with cells programmatically.

In this answer, I detail the Swift code to produce an NSTableCellView programmatically: https://stackoverflow.com/a/51736468/5951226

However, if you don't want all the features of an NSTableViewCell, note that you can return any NSView in tableView(_:viewFor:row:). So you could, as per the CocoaProgrammaticHowtoCollection, simply write:

let cell = NSTextField()
cell.identifier = "my_id" // Essential! Allows re-use of the instance.
// ... Set any properties you want on the NSTextField.
return cell
Jamie Birch
  • 5,839
  • 1
  • 46
  • 60
  • The quote from the docs also says "or found in the reuse queue, this method returns nil." - so it should also work without a nib file - right? – Christian Kienle May 06 '21 at 15:13
  • @ChristianKienle I believe you would only be able to get an instance into the reuse queue in the first place by originally having instantiated a view from a nib. Maybe there is a way to do it entirely programmatically, but I'm not aware of it. – Jamie Birch May 07 '21 at 09:18
  • I also think that your assumption is 100% on point and this is also what I observe. But from reading the docs I think the method is just buggy... Because the table view has all the information it needs to actually properly re-use the cells. but it does not. – Christian Kienle May 07 '21 at 12:02
5

You should set the "Identifier" with "NameCell" in the NSTableCellView. And your codes should simplified as follow since the column's identifier won't change for ever:

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    var result: NSTableCellView
    let cell = tableView.make(withIdentifier: "NameCell", owner: self) as! NSTableCellView
    cell.textField?.stringValue = self.data[row].setting!

    return cell
}

references settings in XCode Interface Builder: enter image description here

clqiao
  • 121
  • 9