Search code examples
swiftmacosnstableviewnstablecolumn

How do I select column and row from a NSTableView?


I am writing a NSTableView based view. And I try to use: let selectedRowNumber = tableViewMesos.selectedRow and let selectedColumnNumber = tableViewMesos.selectedColumn to identify the textField that is in the cell View that I want to save into my array:

taulaDeConsumsMesos[selectedColumnNumber].filesConsum![selectedRowNumber] = sender.doubleValue

But a problem happens here, because the return value of selectedColumnNumber is -1 which is the value of non-selected Column for any row selected of any column. But the selectedRowNumberreturns the index of the row properly.

So the program crashes when finishing the edition of a textfield in a cell. Because the selectedColumnNumber is -1 out of the limits of the array. See the code below to identify where this happens exactly.

Could you help me? I lost too many hours on this.. I'm new on OSX programing I hope you could help me.

The app layout

Here is the completed code:

class BalancGlobalViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate{

@IBOutlet weak var tableViewMesos: NSTableView!

let mesos = ColumnaDeConsumsMes() // contains an array of doubles called .filesConsum that represent the rows that I want to show
let consum = ColumnaDeConsumsMes()

var taulaDeConsumsMesos = [ColumnaDeConsumsMes]() // array of arrays that represent the columns with rows in it that will be the tableView data

func setUpMesos(){ // init table columns
    mesos.filesConsum = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 10, 11, 12, nil]
    consum.filesConsum = [100, 100, 100, nil, nil, nil, nil, nil , nil, nil, nil, nil, nil]
    taulaDeConsumsMesos.append(mesos)
    taulaDeConsumsMesos.append(consum)
}

func setUpGradient(){
   ...not important here
}

override func viewDidLoad() {
    super.viewDidLoad()

    setUpMesos()

    tableViewMesos.setDelegate(self)
    tableViewMesos.setDataSource(self)

    setUpGradient()

}

override func viewDidLayout() {
    super.viewDidLayout()

    setUpGradient()
}

@IBAction func alEditarUnTextFieldDeConsum(sender: NSTextField) { // this is the IBAction of the TextField of the Consum Column (see the image)

    let selectedRowNumber = tableViewMesos.selectedRow
    let selectedColumnNumber = tableViewMesos.selectedColumn

    print("SelectedRowNumber! =  \(selectedRowNumber)")
    print("SelectedColumnNumber! =  \(selectedColumnNumber)")
    // the selectedColumnNumber return always -1 except when clicking the header of the column, then return 0 or 1 depending on the column. But it should return 0 even when the user edit a row of the index 0 column !!!

    let numero = tableViewMesos.numberOfColumns
    print("numero de columnes: \(numero)") // returns 2

    if selectedRowNumber != -1 {      //-1 is returned when no row is selected in the TableView
        taulaDeConsumsMesos[selectedColumnNumber].filesConsum![selectedRowNumber] = sender.doubleValue  //try to save here the value edited by the user on my array of arrays of doubles 
//here the program crashes! selectedColumnNumber = -1

        print("Sender! =  \(sender.doubleValue)") // the sender returns the information correctly
        //print("SelectedRowNumber! =  \(selectedRowNumber)") // this works too!

        reloadTaula()
    }

}

func tableViewSelectionDidChange(notification: NSNotification) {

    print("columna: \(tableViewMesos.selectedColumn)") 
    print("row: \(tableViewMesos.selectedRow)")
    print("tag: \(tableViewMesos.selectedTag())")
}
// the tableViewMesos.selectedColumn return always -1 except when clicking the header of the colum, then return 0 or 1 depending on the column. But i should return 0 even when I edit a row of the index 0 column !!!

func reloadTaula() {
    tableViewMesos.reloadData()
}


// MARK: - Datasource

func numberOfRowsInTableView(tableView: NSTableView) -> Int {
    if (tableView.identifier == "TaulaMesos") {
        return 13
    } 
}


// MARK: - Delegate

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

    var valorCela: Double
    var cellIdentifier:String=""  

    if tableColumn == tableView.tableColumns[0] {
        if taulaDeConsumsMesos[0].filesConsum![row] == nil {
            return nil
        }else {
            valorCela = taulaDeConsumsMesos[0].filesConsum![row]!
            cellIdentifier = "MesCellID"
        }
    } else {
        if taulaDeConsumsMesos[1].filesConsum![row] == nil {
            return nil
        }else {
        valorCela = taulaDeConsumsMesos[1].filesConsum![row]!
        cellIdentifier = "ConsumCellID"
        }
    }
    if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: nil ) as? NSTableCellView {
        cell.textField?.doubleValue = valorCela
        return cell
    }
    return nil

} 

}


Solution

  • Table views do not support selecting individual cells. Both styles of table view support selecting rows. Old-style NSCell-based table views support selecting columns, but modern view-based table views no longer support selecting columns. But it would never have been the case that both selectedRow and selectedColumn were valid at the same time.

    From the 10.7 AppKit release notes, where view-based table views were introduced:

    Note that the View Based TableView does not support column selection.

    To get the row and column of the cell whose text field sent the action, you should use tableViewMesos.rowForView(sender) and tableViewMesos.columnForView(sender). Note, especially, that there's nothing guaranteeing that the text field that's sending the action method is in a selected row. So, don't think of it as selected. It's just the one that's been manipulated by the user.