Populating a Table translate to Swift ?

Returns NSView but constructed as NSTextField ... How does this translate into Swift ? ...


https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TableView/PopulatingView-TablesProgrammatically/PopulatingView-TablesProgrammatically.html


- (NSView *)tableView:(NSTableView *)tableView
   viewForTableColumn:(NSTableColumn *)tableColumn
                  row:(NSInteger)row {

    // Get an existing cell with the MyView identifier if it exists
    NSTextField *result = [tableView makeViewWithIdentifier:@"MyView" owner:self];

    // There is no existing cell to reuse so create a new one
    if (result == nil) {

         // Create the new NSTextField with a frame of the {0,0} with the width of the table.
         // Note that the height of the frame is not really relevant, because the row height will modify the height.
         result = [[NSTextField alloc] initWithFrame:...];

         // The identifier of the NSTextField instance is set to MyView.
         // This allows the cell to be reused.
         result.identifier = @"MyView";
      }

      // result is now guaranteed to be valid, either as a reused cell
      // or as a new cell, so set the stringValue of the cell to the
      // nameArray value at row
      result.stringValue = [self.nameArray objectAtIndex:row];

      // Return the result
      return result;
}

Accepted Reply

OK, this has gone completely in the wrong direction. Let's go back to your original issue. Well, issues, plural.


The first thing: If you look at this documentation page:


https://developer.apple.com/documentation/appkit/nstableview/1535482-makeviewwithidentifier


you'll see that the method gets a "new or existing" cell. There is absolutely no need to create your own cell manually if "makeViewWithIdentifier" fails. The older documentation that you linked to is essentially wrong on this point.


Instead, if "makeViewWithIdentifier" returns nil, your code should assert, because this is a programmer error — you haven't configured something correctly in IB.


The second thing is that the table cell is almost always a subclass of NSTableCellView. NSTextField is not. When you create a table view in IB, it preconfigures the column to contain a NSTableCellView with a NSTextField as a subview, connected to the parent NSTableCellView's "textField" outlet. You need to return a NSTableCellView, not a NSTextField.


The third thing is that there are typically two identifiers involved: One is the table column identifier, which tells your code which column's cell is wanted in the "viewForTableColumn:" method. The other is the table cell identifier, which your code uses to tell "makeViewWithIdentifier" which cell you want. By default, a table view is created in IB with these two identifiers automatically locked together. You would normally write something like this:


let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: self) as! NSTableCellView
cell.textField.stringValue = …


This isn't quite right, because "tableColumn" is optional, because this method is also called to get custom row views, and in that case "tableColumn" is nil. Your actual code, therefore, will look more like this:


guard let identifer = tableColumn?.identifier else { return nil }
let cell = tableView.makeView(withIdentifier: identifer, owner: self) as! NSTableCellView
cell.textField.stringValue = …


If you find this crashing because of the "as! NSTableCellView", that probably indicates that your table cell identifier doesn't match your column identifier.

Replies

No need to worry. `NSTextField` is a descendat class of `NSView`, you just return the `NSTextField` as it is. Where do you find difficulty?


    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        // Get an existing cell with the MyView identifier if it exists
        var result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "MyView"), owner: self) as? NSTextField
       
        //...
       
        // Return the result
        return result
    }

As works fine thanks. :-)


I seem to have a problem with the identifier not working. The result is always nil not sure why, I set up an ident for the column but the rest of the hierarchy items are default. Where is the ident being called from ?

I've gone back to the point without using "as? NSTextFleid" and no problem with the identifier. Any ideas ?

I do not understand what sort of issue you are experiencing, but in a simple case as described in the doc, `identifier` seems to be working as expected.


If you find something wrong in your app, please show your current code and settings, as well as what you expect and what you actually get, precisely.

With the initial get result for "withIdentifier" return as text field doesn't work, "if (result == nil)" is always nil.

Possible causes :

- Have you checked you defined the cell Identifier correctly in IB ?

- Have you registered the nib ?

As I mentioned before

"... I set up an ident for the column but the rest of the hierarchy items are default. Where is the ident being called from ?"


Is it referring to the NSTableColumn / NSTableCellView / NSTextField or NSTextFieldCell ?


I haven't setup a nib.

As I wrote I have checked my code works as expectd and returns non-nil value in some cases. If you say YOUR code does not work, we need to see what's the difference between yours and mine.


Please show your current code and settings, as well as what you expect and what you actually get, precisely.


One more, when do you think is the case that macOS returns non-nil value and how have your set up?

Not sure how I can share my settings.


I don't know if this makes any difference but just to add, I was looking at creating mostly programmatically but still have the main part of the table in the IB to add constraints and such.

OK, this has gone completely in the wrong direction. Let's go back to your original issue. Well, issues, plural.


The first thing: If you look at this documentation page:


https://developer.apple.com/documentation/appkit/nstableview/1535482-makeviewwithidentifier


you'll see that the method gets a "new or existing" cell. There is absolutely no need to create your own cell manually if "makeViewWithIdentifier" fails. The older documentation that you linked to is essentially wrong on this point.


Instead, if "makeViewWithIdentifier" returns nil, your code should assert, because this is a programmer error — you haven't configured something correctly in IB.


The second thing is that the table cell is almost always a subclass of NSTableCellView. NSTextField is not. When you create a table view in IB, it preconfigures the column to contain a NSTableCellView with a NSTextField as a subview, connected to the parent NSTableCellView's "textField" outlet. You need to return a NSTableCellView, not a NSTextField.


The third thing is that there are typically two identifiers involved: One is the table column identifier, which tells your code which column's cell is wanted in the "viewForTableColumn:" method. The other is the table cell identifier, which your code uses to tell "makeViewWithIdentifier" which cell you want. By default, a table view is created in IB with these two identifiers automatically locked together. You would normally write something like this:


let cell = tableView.makeView(withIdentifier: tableColumn.identifier, owner: self) as! NSTableCellView
cell.textField.stringValue = …


This isn't quite right, because "tableColumn" is optional, because this method is also called to get custom row views, and in that case "tableColumn" is nil. Your actual code, therefore, will look more like this:


guard let identifer = tableColumn?.identifier else { return nil }
let cell = tableView.makeView(withIdentifier: identifer, owner: self) as! NSTableCellView
cell.textField.stringValue = …


If you find this crashing because of the "as! NSTableCellView", that probably indicates that your table cell identifier doesn't match your column identifier.

Still, you can share your code. And you can share how you setup to get non-nil value.

Tried to re-create what I had in the first place but can't, not sure why. As NSTableCellView works fine.


Thanks.