Search code examples
swiftcocoanstableview

Access value of NSTableView


I need to access to values of my cell based TableView from another controller. I do this code in my main controller and the tableView is in a child controller.
Here is what I do but I'm getting nil ...

// First grab the tableView controller
let myTableViewController = self.children[0] as! MyTableViewController

// Now I want to access the first row to get values of the textFields
let firstRowView = myTableViewController.theTable.rowView(atRow: 0, makeIfNecessary: true)

//Let's try another way :
let firstView = myTableViewController.theTable.view(atColumn: 0, row: 0, makeIfNecessary: true)

//I can't do more because "firstRowView" and "firstView " are nil

enter image description here

What I want is an array that contains all string of the table like :

let array1 = ["Peter","Quinn","1","387","49"]

EDIT :

I tried with a view base tableView, it works, I can get the textFields in my array.
But if edit a textfield by clicking ont it (changing "Peter" by "Franck"), it doesn't work, the NSTextField disappears in the debug utility...

let firstRowView = myTableViewController.theTable.rowView(atRow: 0, makeIfNecessary: true)?.subviews // return the first TableView
let textFields = firstRowView!.map { $0.subviews } as! [[NSTextField]]
let texts = textFields.map {$0.first!.stringValue}

print(texts) // let array1 = ["Peter","Quinn","1","387","49"]

Why I doesn't work anymore if I change manually a text ?

Here is what "firstRowView" contains when I don't manually edit a cell : enter image description here

And here is what "firstRowView" contains when I manually edit a cell and it crashed : enter image description here

You can see the NSTableCellView is transformed into NSView and my textField has disappeared... (it's still there in UI of course...)


Solution

  • First of all never get data from the view (the table view), get it from the model (the data source array).

    In macOS there is a very convenient way. Use Cocoa Bindings and a view based table view. Apart from the model there's almost no code needed.

    • Create a model, a class inheriting from NSObject is mandatory to take advantage of Key-Value Coding

      @objcMembers
      class Person : NSObject {
          dynamic var firstName, lastName : String
          dynamic var age : Int
          dynamic var number1, number2 : Int
      
          init(firstName : String, lastName : String, age : Int, number1 : Int, number2 : Int) {
              self.firstName = firstName
              self.lastName = lastName
              self.age = age
              self.number1 = number1
              self.number2 = number2
          }
      }
      
    • Declare a data source array

      @objc dynamic var people = [Person]()
      
    • and populate it

      override func viewDidLoad() {
          super.viewDidLoad()
          people = [Person(firstName: "Peter", lastName: "Quinn", age: 1, number1: 387, number2: 49),
                    Person(firstName: "Peter", lastName: "Quinn", age: 1, number1: 387, number2: 49)]
      }
      

    That's the entire code. Now do the bindings.

    In Interface Builder first select the table view (the easiest way is to ⌃⇧-click into the table view area and select Table View), go to Bindings Inspector (press ⌥⌘7) and bind the Content to View Controller and set Model Key Path to people.

    enter image description here

    Then select the text fields one by one – again ⌃⇧-click on each text field in the table view and select Table View Cell – and bind the Value to Table Cell View and set Model Key Path to objectValue.<property> for example

    enter image description here

    That's all. All changes in the table view (for example editing a field) are synchronized with the model. If you want to get the first name of the second entry just write

    let secondFirstName = people[1].firstName