Search code examples
swiftmacosnstableviewappkit

How to create a custom NSTableCellView from a NIB?


I'm new to Swift and I am struggling with NSTableView! I'm trying to create a custom NSTableCellView from a NIB.

I want to load the cell from a NIB because:

  • it will be reused in multiple columns and multiple table-views
  • it will be visually and functionally complex (relatively)
  • it is likely to evolve during development

I'm able to load the cell in my table view but I'm getting a "Failed to connect outlet from... missing setter or instance variable" error in the debug area when I try to populate the view with data. The custom cell is visible in the tableview but doesn't seem to be instantiated.

I've searched for a solution online for hours! Help what am I missing?

This is my TableViewController...

protocol TableViewDelegate {
    func itemWithIndexWasSelected(value: Int)
}

class TableViewController: NSViewController {

    @IBOutlet weak var tableView: NSTableView!

    let tableViewData =
        [  [ "Column1": "John", "Column2": "Smith", "Hobby": "Birds"],
           [ "Column1": "Jane", "Column2": "Doe", "Hobby": "Fish"],
           [ "Column1": "Hal", "Column2": "Bernard", "Hobby": "Trees"],
           [ "Column1": "Harry", "Column2": "Bell", "Hobby": "Rocks"] ]


    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
    
        let customCellNib = NSNib.init(nibNamed: "CustomTableCellView", bundle: nil)
        tableView.register(customCellNib, forIdentifier: NSUserInterfaceItemIdentifier("CustomCellView"))
        tableView.reloadData()
    } 
}

extension TableViewController: NSTableViewDataSource, NSTableViewDelegate {

    func numberOfRows(in tableView: NSTableView) -> Int {
        return tableViewData.count
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    
        if tableColumn?.identifier.rawValue == "CustomCell" { 
            let result: CustomTableCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("CustomCellView"), owner: self) as! CustomTableCellView
        
            result.hobbyLabel?.stringValue = tableViewData[row]["Hobby"]!
            result.hobbyButton?.title = "TESTTITLE"
        
            return result
        }

        else {
            let result = tableView.makeView(withIdentifier:(tableColumn?.identifier)!, owner: self) as! NSTableCellView
            result.textField?.stringValue = tableViewData[row][(tableColumn?.identifier.rawValue)!]!
        return result
        }
    } 
}

I have a CustomTableCellView with a XIB that has the same name...

class CustomTableCellView: NSTableCellView {

    @IBOutlet weak var hobbyButton: NSButton!
    @IBOutlet weak var hobbyLabel: NSTextField!

}

I have a test project that I can send or upload... help will be hugely appreciated!

This is what I am seeing:

enter image description here


Solution

  • When editing a xib, the File's Owner proxy object represents the object which is passed as owner to makeView(withIdentifier:owner:) at runtime. In this case the owner object is the view controller. You can set the class of the File's Owner in the xib to TableViewController and connect actions. You can't set the class of the File's Owner to CustomTableCellView and connect outlets. Instead connect the outlets of the CustomTableCellView view object.