1
votes

I may be lost in a glass of water but I can't seem to be able to add columns to a NSTableView that are then recognised in the NSTableViewDelegate. I create a table in IB with one column and give the column a string identifier. The I add the other columns in the View Controller:

 override func viewDidLoad() {
        super.viewDidLoad()

        for columnIndex in 0..<blotter!.singleOutput[0].parameter.count  {

            let tmpParam = blotter!.singleOutput[0].parameter[columnIndex]
            let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: tmpParam.columnID))
            column.title = tmpParam.label
            column.width = CGFloat(80)
            column.minWidth = CGFloat(40)
            column.maxWidth = CGFloat(120)
            blotterOutputTable.addTableColumn(column)

            }

        blotterOutputTable.delegate = self
        blotterOutputTable.dataSource = self
        blotterOutputTable.target = self

        blotterOutputTable.reloadData()

    }

The NSTableViewDataSource returns the correct number of rows. The problem I have is in the NSTableViewDelegate:

extension OutputsViewController: NSTableViewDelegate {

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

        var text: String = ""
        var cellIdentifier: String = ""

        guard let item = blotter?.singleOutput[row] else {   return nil  }

        // 1. LABELS COLUMN
        // ================

        if tableColumn?.identifier.rawValue == "dealColumn" {

            let myParameter = item.parameter.index(where: {$0.columnID == "BBTickColumn"})

            text =  item.parameter[myParameter!].value as! String               
            cellIdentifier = "dealColumn"

            if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {

                cell.textField?.stringValue = text                  
                return cell
                }

            else {    return nil  }

            }   // END OF LABLES COLUMN (FIRST ONE)




        else {     // THIS IS WHERE THE PROBLEM IS

            let myParameter = item.parameter.index(where: {$0.columnID  ==  tableColumn?.identifier.rawValue } )
            let (_, valueAsText) = item.parameter[myParameter!].interfaceItems()
            text = valueAsText

            cellIdentifier = item.parameter[myParameter!].columnID


            if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {

                cell.textField?.stringValue = text
                return cell
                }

            else {    return nil  }  // DEBUGGER PARAMETER ARE FROM HERE

            }      
        }

}

The first column is the one I created in IB with its identifier. That works. The problem I have is in the else statement (which does not check for a column identifier). Below are the parameters as I see them in the debugger window when I stop the program after the cell creation failed

tableView   NSTableView 0x000000010ebf9df0
tableColumn NSTableColumn?  0x0000600000895770
row Int 0
self    DataBaseManager.OutputsViewController   0x0000600000102b50
text    String  "FLAT"  
cellIdentifier  String  "directionColumn"   
item    DataBaseManager.BlotterOutputs  0x000060000002c240
myParameter Array.Index?    0
valueAsText String  "FLAT"  
cell    (null)  (null)  (null)
tableColumn NSTableColumn?  0x0000600000895770
tableColumn?.identifier NSUserInterfaceItemIdentifier?  some
_rawValue   _NSContiguousString "directionColumn"   0x000060000104d200
Swift._SwiftNativeNSString  _SwiftNativeNSString    
_core   _StringCore 

You can see that cellIdentifier and the tableColumn?.identifier.rawvalue are the same string (as it should be). I cannot understand then why the cell is not created. Any help is mostly welcome and let me know if this is not clear. Thanks

1
Does the tableview in the Storyboard/XIB contain a cell view with id "directionColumn"?Willeke
Than you for your reply Willeke. The answer is no. Only the first column is in IB ("dealColumn"). All the other columns are added in viewDidLoad(). Their identifiers are in a class (parameter) which has a columnID property. It is the one that is used as the identifier for the newly created column. The debugger shows directionColumn because that is the first column that is created (and therefore where I stop the program to debug). I have about 50 columns to create, hence why I do not want to put them all in IB but add them programmatically.Marco
I have looked at it and can't seem to make it working. Specifically I have modified viewDidLoad() to include the nib lines:Marco
But on the line let myCellViewNib = blotterOutputTable.registeredNibsByIdentifier!["dealColumn"] get an error message: Cannot subscript a value of type '[NSUserInterfaceItemIdentifier : NSNib]' with an index of type 'String'. Apologies if this is trivial, but I am not a professional developer and I don't have a lot of experienced. Thank youMarco

1 Answers

0
votes

must register nibs identifiers as in this code:

import Cocoa

class MultiColumnTable: NSViewController, NSTableViewDataSource, NSTableViewDelegate {

    var list = [[String]](), header=[String]()
    var tableView : NSTableView? = nil
    var nColumns : Int = 0

    func genID(col : Int) -> NSUserInterfaceItemIdentifier { // generate column ID
        return NSUserInterfaceItemIdentifier(rawValue: String(format: "Col%d", col))
    }

    func setContent(header: [String], list : [[String]]) {
        self.header = header
        self.list = list
        self.nColumns = list[0].count

        if tableView != nil {
            tableView?.reloadData()
        }
    }

    func numberOfRows(in tableView: NSTableView) -> Int {

        func createColumns() {
            func addColumn(col:Int, header:String) {
                let tableColumn = NSTableColumn(identifier: genID(col: col))
                tableColumn.headerCell.title = header
                self.tableView!.addTableColumn(tableColumn)
            }

            // create columns and register them in NIB
            // IB: tableColumn[0] identifier ( NSTableColumn to "Col0" )

            if let myCellViewNib = tableView.registeredNibsByIdentifier![NSUserInterfaceItemIdentifier(rawValue: "Col0")] {

                for col in 0..<nColumns { // table should have 1 col in IB w/Ident 'Col0'
                    addColumn(col: col, header: header[col])
                    tableView.register(myCellViewNib, forIdentifier:  genID(col: col))  // register the above Nib for the newly added tableColumn
                }
                tableView.removeTableColumn(tableView.tableColumns[0])  // remove the original Col0
            }
        }

        self.tableView = tableView
        createColumns()

        return list.count
    }

    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?.textColor = NSColor.blue
            return cell
        }
        return nil
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
    }
}