How to populate an NSTableView in code

Screen Shot 2014-05-04 at 11.22.52I’ve previously shown you how to populate an NSTableView using Bindings and an array controller. Today I’ll show you how to do it in code.

It’s relatively simple and very similar to powering a UITableView. The main difference is that in Cocoa we don’t have a controller object that comes bundled with our view, so we’ll have to create one manually (or use an existing class). Here are the steps:

  • add an NSTableView to your XIB file
  • create a controller class based on NSObject (I’ll call mine TableController)
  • drag an NSObject object into the XIB (that blue box)
  • associate it with your custom controller object (i.e. TableController)
  • set the data source and implement its methods

In this example I’m using the numbers 1-10 in two arrays: as numbers, and as written out values – so that two columns of the table view can be populated with different items.

#pragma mark - Custom Initialisers

- (NSArray *)numbers {
    if (!_numbers) {
        _numbers = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10"];
    return _numbers;

- (NSArray *)numberCodes {
    if (!_numberCodes) {
        _numberCodes = @[@"One", @"Two", @"Three", @"Four", @"Five", @"Six", @"Seven", @"Eight", @"Nine", @"Ten"];
    return _numberCodes;

Next we’ll implement the following two methods: the first for returning the number of rows, and the second for returning the relevant data in each row:

#pragma mark - Table View Data Source

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    // how many rows do we have here?
    return self.numbers.count;

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    // populate each row of our table view with data
    // display a different value depending on each column (as identified in XIB)
    if ([tableColumn.identifier isEqualToString:@"numbers"]) {
        // first colum (numbers)
        return [self.numbers objectAtIndex:row];
    } else {
        // second column (numberCodes)
        return [self.numberCodes objectAtIndex:row];

Notice that in the second method we’re not referencing the columns with a number (like we do with rows). That’s because the user could reorder or remove columns. Instead we’re using the column identifier which you can set in IB.

Reacting to selections

If you want to react to user selections in your code you need to implement the NSTableViewDelegate Protocol and also connect your table view’s delegate to the above class. Then simply implement this method and you’re notified when a user clicks a row:

#pragma mark - Table View Delegate

- (void)tableViewSelectionDidChange:(NSNotification *)notification {
    NSTableView *tableView = notification.object;
    NSLog(@"User has selected row %ld", (long)tableView.selectedRow);

Unlike in iOS, we’re not directly passed the table view object – but we can grab it from the notification. Note that the log message will be off by one because the rows start at 0 rather than 1.

Watch the Screencast

Here’s a demo project with the above steps implemented:

22 thoughts on “How to populate an NSTableView in code

    1. Invaluable advice, Charan. However this article and the video are about how to build a table view on the Mac, not on iOS. I thought the screenshot showing a Mac Table View was a dead giveaway, much like the video and the entire article. Guess you didn’t get a chance to check any of those…

  1. Thanks for posting this. I’ve found so many IOS tutorials but hardly any ones for mac desktop development. Got me started for my project (I’m new to mac development)

  2. Thanks for the tutorial. I went through you tutorials and learn a lot but Can you please tell me if you have populate the table for more than one column (couldn’t found any ). Without the use of controller (if it is possible) and view based.
    Thanks once again. 🙂

    1. Hi Sahil,

      I’m not sure if I understand you correctly. Say you had two columns, and you only populate one of them, the data will show fine in the first column but not in the second. It’s not an error as such, the second column would just be empty. In the example I’m showing how to populate two columns with two arrays, but you could also populate more columns using additional arrays.

  3. @interface TableController : NSObject will give me error. Could not find protocol declaration for “NSTableViewDataSource” and “NSTableViewDelegate” .

    1. Hi Sebastian,

      you need to specify that your class conforms to both those protocols. In your header file, you’re probably missing this:

      @interface TableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
          1. Is curious that your demo project works perfect. I found a temporary resolution. I #import and the error disappear. Also I saw that your project the Cocoa is present at Linked Framework and Libraries.

  4. I am having the same issue as Sebastian. I don’t know environment Sebastian was using but could it be that I am using Xcode 6? Maybe they have changed the way that this works?

  5. I am using Xcode 6.1 with a storyboard. When I CTRL-drag to the NSObject which I placed into the Application Scene and renamed TableController, the item does not turn blue. When I release to open the datasource/delegate bubble, nothing happens. Suggestions?

  6. I had both of the same problems that Sebastian had when trying this tutorial on Xcode 7.3.1. I had to import Cocoa.h into the TableController class in order to be able to make it conform to the NSTableViewDataSource. I believe this is because starting with Xcode 6, new projects are not created with a pch file. In Xcode 5, a pch file with cocoa.h imported was created, but not in Xcode 6 and up. I also had the problem where all the table view cells said “Table View Cell”. I’m not exactly sure what the problem is, but it seems that in Xcode 6, the structure of the NSTableView changed and it causes that problem. I copy and pasted the table view from the github project’s xib (after cloning it) and it worked fine after that.

Leave a Reply