I am able to create simple view based NSTableViews but there's one point I don't understand about identifiers.
In an NSTableView you typically give a column an identifier and then implement the delegate method:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
And then you switch on the column to do what you need, something like:
switch tableColumn!.identifier {
case "firstColumn":
//do something...
case "secondColumn":
//do something else...
default:
return nil
}
However additionally you can give each Table Cell View an identifier as well. So in the above example, say I didn't give the identifier to the column, and instead gave the identifier to the Table Cell View itself.
I presumed that then I could do something like this in the delegate method:
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
This works, but never makes it past the first if let
statement, and so all my cells say "Hi! I'm in the first column"
More Info:
Something else I don't understand: it seems that the Table Cell View identifier overrides the identifier to the column.
If I go to the document outline and assign identifiers something like this:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn"
tableViewCell: "secondColumnCell"
and then supply both the column identifier and the cell identifier, it works!
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
case "secondColumn":
if let secondColumnCellView = tableView.make(withIdentifier: "secondColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the second column"
return view
} else {
return nil
}
default:
return nil
}
But it crashes if I allow the switch statement to ignore the cell identifier for the second column, and fall through to trying to use the column identifier.
switch tableColumn!.identifier {
case "firstColumn":
if let firstColumnCellView = tableView.make(withIdentifier: "firstColumnCell", owner: self) as? NSTableCellView {
view.textField?.stringValue = "Hi! I'm in the first column"
return view
} else {
return nil
}
default:
break
}
let cellView = tableView.make(withIdentifier: tableColumn!.identifier, owner: self) as! NSTableCellView
cellView.textField?.stringValue = "hello"
return cellView
//CRASH: Unexpectedly found nil when unwrapping tableColumn!.identifier
// The column both exists and has an identifier of "secondColumn", so how could
//this be nil?
And it seems I can confirm this overriding behavior by renaming the secondColumnCell to the same name as the secondColumn:
tableColumn: "firstColumn"
tableViewCell: "firstColumnCell"
tableColumn: "secondColumn" <--- Same Name
tableViewCell: "secondColumn" <-- Same Name
And now the code runs as expected and doesn't crash.
If I read your last chunk of code correctly, you instantiate (or retrieve from a used-views-no-longer-on-screen pool - see here) a view with the identifier firstColumnCell.
As long as the identifier is valid (you have somewhere a nib defining the view) the method will always return a non-nil view so the first
if let ...
will always succeed.So the
view.textField?.stringValue = "Hi! I'm in the first column"
will execute thus showing the message in the cell and then it will return the view to be used by the NSTableView and exit your method.The next
if let ...
statements will never have a chance to execute.