Category Archives: macOS Development

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:





How to bind an NSArrayController to an NSTableView

Core-Data-IconIn this screencast I will show you how to bind a Table View to an Array Controller in Cocoa, using Xcode 5.1 and OS X Mavericks.

We’re using Core Data to save our entries and – check it out – we’re not writing a single line of code!

Cocoa Bindings is one of the most exciting features in OS X development for me, and I hope that one day it’ll find its way into iOS too.

The project is also available on GitHub:

I’ve written more about how to do this here:

 





Binding an NSTableView to Core Data without code

Mountain Lion LogoI was excited to find out that it is possible to write a Mac App with Core Data completely without code! This magic is possible with something that’s not available in iOS (yet) called Cocoa Bindings.

You provide the user controls you need in Xcode, then control-drag your way to extensive functionality and happiness. Before I forget, I thought I’d better make some notes.

Here’s a step-by-step list:

  • Using Xcode, create a new Mac Cocoa Application with Core Data
  • Setup your entities and properties in your Core Data Model (.xcdatamodeld)
  • Create relationships between Entities if desired
  • Select all Entities and create custom classes from them (using Editor – create NSManagedObject Subclasses)

Drag our friend the Array Controller onto the Object’s Bar. If you have multiple Entities, you need a separate Array Controller for each one. Change the labels to tell them apart (select a different file and go back to the xib in order for those labels to be visible – it’s a bug in Xcode).

Now bind the Array Controller and Core Data Managed Object Context together:

  • Select your Array Controller, then go to the Bindings Inspector (second from the right)
  • Under Parameters, head over to the Managed Object Context and bind to the App Delegate (as that’s where Xcode has kindly prepared our Core Data stack)
  • In Model Key Path, type self.managedObjectContext (this should self complete)
  • In the Attributes Inspector, tell the controller that we’re using an Entity instead of a Class, then enter the Entity (it’s the one you’ve created in the data model)
  • While you’re here, tick the “Prepares Content” check box if you want the table view to be populated automatically

Add a Table View to your window and bind it to the Array Controller:

  • Select the Table View (inside the Scroll View) and head over to the Bindings Inspector
  • Under Table Content, select Bind To your Array Controller
  • Now select the actual Table Column you want to fill with data, and still inside the Bindings Inspector under Value, bind the column to your Array Controller
  • In the field for Model Key Path, type the attribute/property you’d like to see in this column (you’ve defined this in the data model)
  • Repeat the process for each column you want to fill with data

To add data to your table:

  • Add a couple of Gradient Buttons to your window
  • Make one the “Add” and one the “Delete” button
  • Hook up each button to the Array Controller via control drag, selecting add and remove respectively

Congratulations: You’ve just created an editable Table View, hooked up to Core Data without a single line of code!

Let’s create a functional SAVE menu item

If you quit the app (rather than running it again from Xcode), all your data will save. However we can implement a manual save action with ease. The menu item itself is there by default, all we need to do is hook it up to the App Delegate and bind it to the save function.

In the Object Sidebar, select Main Menu, then select File – Save (which will expand the item in the sidebar too). From the save menu option, control drag to the App Delegate and select saveAction.