NSTableView Tips: View Controller per row

Cocoa, Coding

Sometimes you may find your application has rather complex rows, and you want an NSViewController to manage each row in a “View Based” NSTableView. This is fairly easy to do, and I did it in my Cyr Wheel pattern editing application. My “Cell View” that I want for each row was designed in Xcode like:

Screen Shot 2014-04-26 at 8.52.47 PM.png

A simple NSViewController subclass:

@interface CDPatternItemViewController : NSViewController
   // the thing we are editing
   @property(weak) CDPatternItem *patternItem;

The controller code is what is interesting. I use an NSWindowController subclass, and keep an array of the view controllers:

@interface CDPatternEditorWindowController () {
  NSMutableArray *_patternViewControllers;

I decided to lazily load the view controllers. On initialization/load of my window I fill the array with NULL:

- (void)_resetPatternViewControllers {
  _patternViewControllers = NSMutableArray.new;
  for (NSInteger i = 0; i < self._patternSequence.children.count; i++) {
    [_patternViewControllers addObject:[NSNull null]]; // placeholder
  [_tableView reloadData];

_patternSequence is simply a wrapper around my model object, stored in my Core Data backed NSDocument; it has an array of things I want to show in the table

- (CDPatternSequence *)_patternSequence {
   return self.document.patternSequence;

Then, the view based tableview method that requests the view simply creates and caches it:

- (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {
    id currentObject = [_patternViewControllers objectAtIndex:index];
    CDPatternItemViewController *result;
    if (currentObject == [NSNull null]) {
        CDPatternItem *item = [self._patternSequence.children objectAtIndex:index];
        result = [CDPatternItemViewController new];
        result.patternItem = item;
        [_patternViewControllers replaceObjectAtIndex:index withObject:result];
    } else {
        result = (CDPatternItemViewController *)currentObject;
    return result;

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];
    // don't set the identifier so it isn't reused...
    return vc.view;

This should be fairly straightforward, and easy to read. But if you have questions let me know and I will clarify.

I don't cleanup the view controllers when the items are removed from the table. I could use the NSTableView delegate method tableView:didRemoveRowView:forRow: to drop my NSViewController cache at this time if I so wanted. However, my table is small..and I don't bother with this.

Notify of

Inline Feedbacks
View all comments
Joanna Carter

Hi Corbin. I have been using a similar approach for iOS table views. I have a project with several static table views, most of which have fairly complex interactions. I derive my controllers from NSObject since there really isn’t an equivalent of UIViewController for a UITableViewCell, although I guess I could have made the contentView the controller’s view.

I would like to ask what you think you gain from using an NSViewController over just a plain old NSObject?


Hi Joanna,
The NSViewController automatically loads the view in my case; it does the grunt-work nib loading. It also allows me to know when the view loaded and do stuff. I also have other reasons, which I’ll document later.


Joanna Carter

Hi Corbin
That makes sense. My “CellController” has a cell property and looks after loading it from the nib in much the same way as the view controller would. I think we are working along very similar lines.

Given the time, the only improvement I would like to add is to be able to incorporate a reuse identifier for larfer, dynamic tables.

Subscribe to new posts:

You'll get an email whenever a I publish a new post to my blog and nothing more. -- Corbin

As an Amazon Associate I earn from qualifying purchases.

(c) 2008-2024 Corbin Dunn

Privacy Policy

Subscribe to RSS feeds for entries.

78 queries. 1.170 seconds.

Log in