Corbin's Treehouse - Corbin Dunn, Santa Cruz, CA
Plug Bug

NSTableView Tips: View Controller per row

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 =;
  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.

3 Responses to “NSTableView Tips: View Controller per row”

  1. Joanna Carter says:

    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?

  2. corbin says:

    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.


  3. Joanna Carter says:

    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.

(c) 2008-2017 Corbin Dunn

Corbin's Treehouse is powered by WordPress. Made on a Mac.

Subscribe to RSS feeds for entries and comments.

39 queries. 0.416 seconds.