Parse Data into Nstableview

Parse Data into NSTableView

Yes, tableviews with Cocoa are... complicated.

The absolute basics for getting content in the tableview is to set its datasource, which has to adopt NSTableViewDataSource protocol. Then you have to differentiate between a view based and a cell based tableview. In the view based you can use a view you designed in Interface Builder, the cell based are older and simpler. You understand the numberOfRowsInTableView function, so I proceed with the more complicated functions.

Cell based TableVies

For cell based tableViews, the second essential function## Heading ## is

func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!

When you just got it, it's pretty easy. As first parameter you get the tableView (only interesting if you use the same dataSource for multiple tableViews).
The second specifies the tableColumn. You can identify a column by using its identifier. You set this identifier in the InterfaceBuilder. Click on your tableview until a column is selected. Then set in the sidebar the Restoration ID.
As last parameter you get your row. You return an object of type AnyObject. I normally return a string, I don't know whether NSNumber is also valid.
A simple example implementation is the following:

func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!{
var result = ""

var columnIdentifier = tableColumn.identifier
if columnIdentifier == "number" {
result = "\(row+1)"
}
if columnIdentifier == "name" {
result = model.persons[row].name
}
if columnIdentifier == "lastName" {
result = model.persons[row].lastName
}
return result
}

When you want to set values, use

func tableView(tableView: NSTableView!, setObjectValue object: AnyObject!, forTableColumn tableColumn: NSTableColumn!, row: Int)

Object represents the new value you should transfer to your data model.

View based TableViews

In view based tableViews, the things lays different.

Here you have to design a custom view in a separate nib file. Then, you register the nib in your initializer.

let nib = NSNib(nibNamed: "Example", bundle: NSBundle.mainBundle())
self.tableView.registerNib(nib, forIdentifier: "Example")

This allows you to create instances of your view using makeViewWithIdentifier:owner:
Then you can configure your view (I like to subclasses NSView for this) and pass it back as result of

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

Example implementation:

func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView!{
let view = tableView.makeViewWithIdentifier("Example", owner: self) as MyCustomView
view.field1.stringValue = model.persons[row].name
view.field2.stringValue = model.persons[row].lastName
view.field3.stringValue = "\(row+1)"
return view
}

Getting array objects into NSTableView

The important part of the code you need is the DataSource delegate functions:

extension ViewController : NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return devices.count
}

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

// 1 - get the device for this row
guard let device = devices[row] else {
return nil
}

// 2 - configure the cell with the device data
return nil
}

There is an example here on StackOverflow that should give a better example

Reloading NSTableView after downloading data

PFQuery works asynchronously, the data is returned much later - in terms of computer speed – after viewDidLoad exits.

Reload the table view in the block right after the array has been populated on the main thread.

var teachers = [Teacher]()

override func viewDidLoad() {
super.viewDidLoad()
tableView.setDelegate(self)
tableView.setDataSource(self)
let query = PFQuery(className: "TeacherList")
query.findObjectsInBackgroundWithBlock { [unowned self] (objects, error) in
if let objects = objects {
for object in objects {
let teacher = Teacher()
teacher.name = object["name"] as! String
teacher.email = object["email"] as! String
teacher.subjectsTaught = object["subjectsTaught"] as! [String: String]
teacher.category = object["category"] as! String
teacher.uniqueID = object.objectId!
self.teachers.append(teacher)
}
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
print(self.teachers)
}
}

Crash by adding data to NSTableView

As you are using Cocoa Bindings which is based on Key-Value Observing you have to put any line which affects the UI into a DispatchQueue.main block

DispatchQueue.main.async {
self.Series = []

for (_,subJson):(String, JSON) in theJSONContent {

let serie = Serie(
theID: subJson["id"].stringValue,
serieName: subJson["seriesName"].stringValue,
serieAired: subJson["firstAired"].stringValue,
serieNetwork: subJson["network"].stringValue,
serieStatus: subJson["status"].stringValue
)

self.Series.append(serie)
}
self.serieTabla.reloadData()
}

Illegal NSTableView data source

The tutorial is written in Swift 2. Your code seems to be Swift 3.

The signature of numberOfRowsInTableView is wrong. In Swift 3 it's

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

PS: It is preferable to connect delegate and data source in Interface Builder rather than in code.

How do I load an XML document into an NSTableView?

The answer given by shreyasva is close but somewhat misleading.

First, parsing the XML into an easily-managed Cocoa data structure is perfectly correct. For performance reasons, you shouldn't be tying your table's datasource directly to the XML. yan.kun's suggestion is certainly possible but if you have "more than a little" data, you very well could run into performance problems. I highly recommend just parsing the data into an NSArray of NSDictionary objects for longer data sets.

Second, Core Data is a bit overkill if you don't plan to persist the XML document in some other way or if you only have a handful of objects. Overkill by a long shot. It's also not necessary (and often not reasonable) to shoehorn every data structure in your app into Core Data without good reason. An NSDictionary instance will work just fine for caching the parsed data for consumption by a table view.

Third, there is no -tableView:cellForRowAtIndexPath: method. This seems to be confusing NSTableView with UITableView. Since you specified the Mac tag, look into the NSTableViewDataSource protocol. Cocoa Bindings is not "better than" or a "replacement for" the data source protocol. It's an "alternative to". You can either load your parsed data into an NSArrayController (an array of dictionaries, one per "record", for example) and bind the table columns to it (each column is bound to a key in the dictionaries in the array controller's arrangedObjects) or just use the (easy) table data source protocol that takes literally two minutes of copy/paste from the docs to get up and running.



Related Topics



Leave a reply



Submit