Swift – NSTableView Sample Code
Updated on 22-May-2017
Please read http://www.knowstack.com/swift-3-1-nstableview-complete-guide/ for checking the different approaches of loading a NSTableView in Swift 3.1
- Using Xcode 6.0 Create a new cocoa project and select the language as Swift
- Modify the MainMenu.xib by
- Adding a NSTableView from the library
- Set the AppDelegate as the delegate and datasource for the table view
- add two columns in the tableview and name them as FirstName and LastName
- State the identifier of the two columns to FirstName and LastName respectively. We will be using an Array of Dictionaries with keys as FirstName and LastName
- Modify the AppDelegate.swift class by adding the below code
Note: For the below code to work, the table view needs to be cell based (not view based). Scroll down for the updated example that includes a column containing checkboxes as well. That is the working code.
// // AppDelegate.swift // SwiftTableViewLoading // // Created by Debasis Das on 6/2/14. // Copyright (c) 2014 KnowStack. All rights reserved. // import Cocoa //In this sample code we are making the AppDelegate the datasource and delegate for our table class AppDelegate: NSObject, NSApplicationDelegate,NSTableViewDataSource,NSTableViewDelegate { @IBOutlet var window: NSWindow @IBOutlet var myTableView: NSTableView func applicationDidFinishLaunching(aNotification: NSNotification?) { // Insert code here to initialize your application } func applicationWillTerminate(aNotification: NSNotification?) { // Insert code here to tear down your application } func numberOfRowsInTableView(aTableView: NSTableView!) -> Int { //let numberOfRows:Int = 20 let numberOfRows:Int = getDataArray().count return numberOfRows } func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject! { // var string:String = "row " + String(row) + ", Col" + String(tableColumn.identifier) // return string var newString = getDataArray().objectAtIndex(row).objectForKey(tableColumn.identifier) return newString; } func getDataArray () -> NSArray{ var dataArray:NSDictionary[] = [["FirstName": "Debasis", "LastName": "Das"], ["FirstName": "Nishant", "LastName": "Singh"], ["FirstName": "John", "LastName": "Doe"], ["FirstName": "Jane", "LastName": "Doe"], ["FirstName": "Mary", "LastName": "Jane"]]; println(dataArray); return dataArray; } }
Output
Swift NSTableView – Editable NSCheckbox Cell
The following code base has an additional column for check box and edit / data state change implemented
import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate,NSTableViewDataSource,NSTableViewDelegate { @IBOutlet weak var window: NSWindow! @IBOutlet weak var myTableView: NSTableView! var dataArray:[NSMutableDictionary] = [[“firstName”: “Debasis”, “lastName”: “Das”, “fullTimeEmp”: 0], [“firstName”: “Nishant”, “lastName”: “Singh”, “fullTimeEmp”: 1], [“firstName”: “John”, “lastName”: “Doe”, “fullTimeEmp”: 1], [“firstName”: “Jane”, “lastName”: “Doe”, “fullTimeEmp”: 1], [“firstName”: “Mary”, “lastName”: “Jane”, “fullTimeEmp”: 0]]; func applicationDidFinishLaunching(aNotification: NSNotification) { // Insert code here to initialize your application } func applicationWillTerminate(aNotification: NSNotification) { // Insert code here to tear down your application } func numberOfRowsInTableView(aTableView: NSTableView!) -> Int { let numberOfRows:Int = dataArray.count return numberOfRows } func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject! { let object = dataArray[row] as NSMutableDictionary if ((tableColumn.identifier) == “fullTimeEmp”) { return object[tableColumn.identifier] as? Int! } else { return object[tableColumn.identifier] as? String! } } func tableView(tableView: NSTableView, setObjectValue object: AnyObject?, forTableColumn tableColumn: NSTableColumn?, row: Int) { dataArray[row].setObject(object!, forKey: (tableColumn?.identifier)!) } }
This tutorial is great, thanks a lot!
I edited the code to have a button that adds an entry to the existing array.
The button works and if I println the array after pushing the button, the array has the new entry at the bottom, but I got stuck while trying to refresh the table view. What is the correct way to refresh the view any time the array is modified?
Thanks again.
I’ve actually managed to do that by using
self.myTableView.reloadData()
May I ask how did you do it so the button adds new entries to the array? I’m a newbie and can’t figure out how to deal with passing data to the array via button action. Thanks a lot!
I’ve found the mistake – just needed to add the data to array as object, not via insert.
Can you update the code to show how to add an entry to the table?
Thanks for the tutorial, but I was wondering how I should do the Key-value binding. I read that there are some problem. Is there any good article or sample code in this topic?
Many thanks for the great code. Do you have a tip on how I can manage two tables within a view?
Hi Abdul,
Till the time the two tables are not dependent in anyways, they can be simply added to a custom view. However if there is a dependency that is desired in terms of synchronizing the scrolls etc. Then both the scroll views containing each of the table view needs to be synchronized to responds to vertical scrolls. There is a good example in developer.apple.com . you can find the article at https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/NSScrollViewGuide/Articles/SynchroScroll.html
Let me know if you have any specific problem, I can write and share the code. Be as descriptive in the problem statement as possible
Great tutorial, Thanks! Do you know a way to manually add a row or if the array of dicts is global and external call adds an additional element to the array to force the tableView to reload?
Thanks!
Hi Nicky,
The table view is UI and the array is the datasource. So once we have set the array as the datasource of the table view, one can simply add a row / object / Dictionary to the array and reload the table view. the new row will start reflecting.
[self.myTableView reloadData]; if the myTableView is declared as a property or [_myTableView reloadData] should do the work]
Any chance you could update this for Xcode 6.1? Can’t get it to work. http://stackoverflow.com/questions/25852994/nstextfield-keeps-repeating-title
sorry for delayed response
Appears that you have figured out the solution and have updated the answer @ stackoverflow.
The conflict would be resolved if the table view was made cell based.
Your help is greatly appreciated, Debasis! Could you extend the example above to show a third column that includes a checkbox for each row? I am unable to control the state of the checkbox from my AppDelegate. I am using an Array Controller to display the contents of an array in AppDelegate in the NSTableView.
Any and all help is greatly appreciated.
Hi Jody,
Sorry for the delayed response.
Have added the code for editing on table and changing state of a checkbox.
You can download the code from the below location
http://www.knowstack.com/wp-content/uploads/2014/06/SwiftTableViewCheckBox.zip
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate,NSTableViewDataSource,NSTableViewDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var myTableView: NSTableView!
var dataArray:[NSMutableDictionary] = [[“firstName”: “Debasis”, “lastName”: “Das”, “fullTimeEmp”: 0],
[“firstName”: “Nishant”, “lastName”: “Singh”, “fullTimeEmp”: 1],
[“firstName”: “John”, “lastName”: “Doe”, “fullTimeEmp”: 1],
[“firstName”: “Jane”, “lastName”: “Doe”, “fullTimeEmp”: 1],
[“firstName”: “Mary”, “lastName”: “Jane”, “fullTimeEmp”: 0]];
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
let numberOfRows:Int = dataArray.count
return numberOfRows
}
func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!
{
let object = dataArray[row] as NSMutableDictionary
if ((tableColumn.identifier) == “fullTimeEmp”)
{
return object[tableColumn.identifier] as? Int!
}
else
{
return object[tableColumn.identifier] as? String!
}
}
func tableView(tableView: NSTableView, setObjectValue object: AnyObject?, forTableColumn tableColumn: NSTableColumn?, row: Int)
{
dataArray[row].setObject(object!, forKey: (tableColumn?.identifier)!)
}
}
Why does this line
var newString:String = getDataArray().objectAtIndex(row).objectForKey(tableColumn.identifier)
not work anymore with my Xcode 6.1.1?
I’m getting ‘Cannot invoke ‘objectForKey’ with an argument of type ‘@lvalue String!’
var id:String = tableColumn.identifier
does work and ‘println(“ID: \(id))’ produces the expected “FirstName” and “Lastname” ;
Try this it should work
let object = dataArray[row] as NSMutableDictionary
if ((tableColumn.identifier) == “your identifier”)
{
return object[tableColumn.identifier] as? Int!
}
Hi Martin,
Try this approach, it should work
You can download the working code from this location
http://www.knowstack.com/wp-content/uploads/2014/06/SwiftTableViewCheckBox.zip
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate,NSTableViewDataSource,NSTableViewDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var myTableView: NSTableView!
var dataArray:[NSMutableDictionary] = [[“firstName”: “Debasis”, “lastName”: “Das”, “fullTimeEmp”: 0],
[“firstName”: “Nishant”, “lastName”: “Singh”, “fullTimeEmp”: 1],
[“firstName”: “John”, “lastName”: “Doe”, “fullTimeEmp”: 1],
[“firstName”: “Jane”, “lastName”: “Doe”, “fullTimeEmp”: 1],
[“firstName”: “Mary”, “lastName”: “Jane”, “fullTimeEmp”: 0]];
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
func numberOfRowsInTableView(aTableView: NSTableView!) -> Int
{
let numberOfRows:Int = dataArray.count
return numberOfRows
}
func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!
{
let object = dataArray[row] as NSMutableDictionary
if ((tableColumn.identifier) == “fullTimeEmp”)
{
return object[tableColumn.identifier] as? Int!
}
else
{
return object[tableColumn.identifier] as? String!
}
}
func tableView(tableView: NSTableView, setObjectValue object: AnyObject?, forTableColumn tableColumn: NSTableColumn?, row: Int)
{
dataArray[row].setObject(object!, forKey: (tableColumn?.identifier)!)
}
}
… and I had to switch the the content mode of my table view from ‘View Based’ to ‘Cell Based’ .
All up and running now! 🙂
That ‘View Based’ to ‘Cell Based’ check needs to be added as an instruction point, other than that, this is a PERFECT tutorial. Nicely done.
Thanks Craig. Glad that you found this useful
Hi,
the following code is not compatible with Swift3. Do you have a tip for me please?
“var newString = getDataArray().objectAtIndex(row).objectForKey(tableColumn.identifier)
return newString;”
Many thanks.
Please refer to the below article for Swift 3.1 Implementation
http://www.knowstack.com/swift-3-1-nstableview-complete-guide/