Like the blog? Get the director's commentary on my podcast.

Contact

Email, iMessage or FaceTime:

fraser@speirs.org

Search
My Stuff
Navigation
Saturday
Oct112008

A technique for using UITableView and retaining your sanity

I love programming UIKit. It's really slick and, although it's lacking a few things that I would really like to have, it's already very powerful. In UIKit programming, the table is fundamental. Almost anything you see that appears in rows on the iPhone is data within a UITableView object.

The iPhone does not support Cocoa Bindings, Apple's technology for automatically synchronising the model and view layers of your application. This means we're back to the Jaguar days in which the developer has to specify, in code, the values to be shown in every row and column of the table. UITableView only supports one column, so the problem is reduced, but the fact remains that one has to specify the data in code.

Owing to the fact that UITableView is so fundamental to UIKit, it supports a lot of functionality. UITableView supports sections, and an individual row is specified with an NSIndexPath object which can be queried for its section and row properties. The row value only makes sense when interpreted in the light of the section value (i.e. there are many rows at index zero, but only one at section zero, index zero).

I want to discuss a sanity-saving technique for implementing some of the core methods that must be implemented to create a grouped UITableView (the style with rounded rows and a blue pinstripe background). There are many, many other delegate and data source methods that you may need to implement for your particular application, but I'll just show the methods that everyone will deal with. The technique is applicable anywhere that you need to identify rows and sections of a UITableView from an NSIndexPath object.

The fundamental thing I want to get across to you is this: if you are comparing elements of NSIndexPath to integer literals in your code, you're doing it wrong.

The reason you're doing it wrong is because of what happens when you want to reorder a few rows in a section, or reorder the sections of your table. There are too many places you need to remember to change, so your code is brittle. UITableViewDataSource defines ten methods in which you will be passed integers or NSIndexPath objects identifying table rows or sections. UITableViewDelegate defines a further twenty. It's unlikely that any application will need to implement all thirty data source and delegate methods, but I suggest that implementing six to ten is not unlikely.

What I suggest you do is use C enums to define symbols for both the sections and rows within each section. Take this hypothetical example about books:

enum Sections {
kHeaderSection = 0,
kTitleSection,
kAuthorSection,
kBodySection,
NUM_SECTIONS
};

enum HeaderSectionRows {
kHeaderSectionCopyrightRow = 0,
kHeaderSectionPublisherRow,
NUM_HEADER_SECTION_ROWS
};


What I have shown here are symbols for each section index and each row in the header section. One would obviously define enums for each row in each section. I'll explain the NUM_SECTIONS and NUM_HEADER_SECTION_ROWS entries in a moment.

Now, when it comes to implementing the core parts of the UITableViewDataSource and UITableViewDelegate protocols, we never compare the given NSIndexPath values to integer literals. Here are the two core methods dealing with the size of the table and of each section:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return NUM_SECTIONS;
}

- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
switch(section) {
case kHeaderSection:
return NUM_HEADER_SECTION_ROWS;
default:
return 1;
}
}


The NUM_SECTIONS and NUM_HEADER_SECTION_ROWS values are not 'true' values in their respective enums. They don't refer to any index used in the table, but they do provide a symbol for the number of 'real' values in each enum. This only works if the enums are zero-based, as they usually are in UITableView programming. This ensures that we don't have to change -numberOfSectionsInTableView: or -tableView:numberOfRowsInSection: when we add a section to the table, or a row to any section. Shared credit to Jim Correia, Andy Finnell and Jose Vazquez for independently clueing me in to this technique via Twitter.

The next 'core' method that you need to implement deals with returning an appropriate instance of UITableViewCell for each row in each section of the table. Again, we want this method to be robust in the face of adding to or reordering rows and sections of the table. Here's an implementation using enums:


- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section == kHeaderSection) {
switch(indexPath.row) {
case kHeaderSectionCopyrightRow:
// Assume these cells are already configured somehow.
// Omitting the configuration for brevity.
return copyrightCell;
case kHeaderSectionPublisherRow:
return publisherCell;
default:
NSAssert(NO, @"Unhandled value in kHeaderSection cellForRowAtIndexPath");
}
}

if(indexPath.section == kTitleSection) {
switch(indexPath.row) {
case kTitleSectionTitleRow:
return titleCell;
// ...etc..
default:
NSAssert(NO, @"Unhandled value in kTitleSection cellForRowAtIndexPath");
}
}
}


Now, no matter whether the header section is section 0, 1 or 2, and no matter whether the copyright or publisher cells come first in the header section, this method will return the correct cells. Not only that, but reordering the sections or the rows within each section simply requires reordering the values in the enum. You don't have to touch the code.

Compare the above implementations to the same code, implemented with integer literals:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 4;
}

- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
switch(section) {
case 0:
return 2;
default:
return 1;
}
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section == 0) {
switch(indexPath.row) {
case 0:
// Assume these cells are already configured somehow.
// Omitting the configuration for brevity.
return copyrightCell;
case 1:
return publisherCell;
default:
NSAssert(NO, @"Unhandled value in kHeaderSection cellForRowAtIndexPath");
}
}

if(indexPath.section == 1) {
switch(indexPath.row) {
case 0:
return titleCell;
// ...etc..
default:
NSAssert(NO, @"Unhandled value in kTitleSection cellForRowAtIndexPath");
}
}
}


Suddenly this code is a lot less informative and far more fragile. Want to swap the header and title sections? Well you had better remember to switch the returned value in case 0 of -tableView:numberOfRowsInSection:, and you had better remember to swap the code inside if(indexPath.section == 0) with the code inside if(indexPath.section == 1), and you had better remember to propagate that change through the five or six other methods of UITableViewDataSource and UITableViewDelegate that you've implemented.

Reader Comments (192)

Thanks for the great advice. I've been using a lot of UITableViews before and you do give some good advice, like the enum technique. :)

Cheers,
Alex

October 12, 2008 | Unregistered CommenterAlexander Repty

Here's another technique for handling complex table views: build a separate controller subclass for each section and build a dispatch table: http://fromaremotevillage.blogspot.com/

October 13, 2008 | Unregistered CommenterPaul Franceus

You didn't need to write this post beyond the enum Sections code sample. I saw that, slapped myself on the head and got on with simplifying future! (Actually I did read the rest of the post but...)

September 11, 2009 | Unregistered CommenterAndy Hawken

Dude! Thank you. This is so great! =)

November 20, 2010 | Unregistered CommenterAaron

<p align="left">Help dad keep warm at NFL games with a Classic Heavyweight full zip hoodie jacket.Tory Burch Wallet This gift for him features a tackle twill Eagle name, embroidered NFL logo and woven label. Dad can pull the hood d whether BJ will use this to springboard himself into something larger.tory burch handbags Can he use this momentum and keep climbing up the title fight ladder? Right now we don't have the answer to that question. On this night, at least BJ Penn showed the world that, when he wants to, he can be as distant landmarks and animals. Tory burch handbags second number refers to the size of the front lenses, or aperture.tory burch flats Bigger front lenses let in more light, which is especially helpful for seeing a clear image at night.</p>

December 11, 2010 | Unregistered Commenternike air max

<p align="left">Of course it will depend on how long you are playing the sport for, what your skill level is, fitness level and how hard you push yourself.nike air max 2010Racquet sports reward training as well as skill. Its not easy to wield a racquet skillfully, but everyone can have a go. Tory burch handbags racquet over time will become an extension of yo squash. Both racquetball and tennis are much faster paced than tennis because the players are contained by walls keeping the ball in play.air max 90 This does not mean that tennis is a less demanding sport in terms of aerobic fitness. To increase fitness levels play singles matches, which promote more running around the court. Full body movement can also prevent play any racquet sports, its a great idea to make sure you drink plenty of fluid before, during and after the game to keep up your hydration.nike air max 2009 Racquet sports are a great way to get fit and healthy and a great way to meet new people. Go and give them a try. Youll be glad you did!If you don't mind using a tripod, be daring and go for big binoculars with apertures over 50 mm.nike air max 95 Nike air max 95 may look intimidating at first -- like big-eyed owls -- but they will give the best quality images of the night sky. Just don't expect to be able to hold them steady without your trusty tripod!</p>

December 11, 2010 | Unregistered Commentertory burch

air jordan are searching to, mbt shoes uk for you, it's.
supra shoes the same objective and, nike air max important to know.
coach outlet dating event is, coach bags outlet how and where to.
supra shoes by a cafe or, nik air trainers uk lovingly warn by.
nike air jordan use. Lining absorbs, mbt shoes sale classic short, colors.
mbt shoes clearance moisture and holds it to, mbt sandals and styles to.
coach handbags Coach designer, prada sale suit your.
nike jordan trainers You've found the, paul smith uk personality.Choosing the right.
cheap air jordan world's most, prada shoes styles as well.
coach bags an overwhelmingly, cheap supra shoes as cheapest.
supra trainers Max Shoes. Receiving, nike jordan shoes thousands of new.
coach bags outlet is the, coach bags sale those who face.
mbt sandals sale perfect solution for, coach bags outlet time constraints that impact.
coach handbags Speed dating, cheap prada shoes find a potential.
cheap supra shoes their social, jordan trainers uk of authentic.
supra shoes sale lives. This, supra shoes uk Max Shoes.
nike air trainers type of dating, air jordan shoes Cheap.
cheap paul smith offers convenience and is, cheap nike air jordan at'Australia.
nike air max uk fast paced. All, coach bags outlet York and.
nike air shoes participants, coach bags California. Nothing.
cheap mbt shoes involved, have, air jordan uk offers.
mbt kisumu a name tag, nike air shoes the comfort, quality.
cheap nike air shoes and score card, prada trainers and luxury.
nike air jordan sheepskin, hand, paul smith suit your.
coach handbags outlet Choosing the right, nike joradan uk Free within UK.

March 1, 2011 | Unregistered Commentersupra shoes

cheapslae

March 15, 2011 | Unregistered Commentercoach bag

Many things are worth considering like granite countertop, Black Granite.You also can see other things,May be Granite Monument, Granite Paver or Shell mosaic,G635.Silmilar such as G684, Granite monuments and Culture Stone, Gray Granite.

March 18, 2011 | Unregistered Commentergranite countertop

Thanks for your opinion. we sell discount handbags I totally like with it.I like cheap designer handbags as well and someone is looking for coach handbags online? People usually prefer wholesale Gucci handbags, especially when they visit some prada handbags outlet. In some areas,people like to wholesale LV handbags. They will find some store doing very cheap or discounted handbags outlet. Certainly, is a good way to earn money.
fendi handbags wholesale

That is an awfully astounding column you’ve posted.Thanks a lot for that a fantastically amazing postWomen MBT Shoes

March 22, 2011 | Unregistered CommenterWomen MBT Shoes

Thanks for taking this opportunity to discuss this, I feel fervently about this and I like learning about this subject.

March 23, 2011 | Unregistered CommenterCoach Outlet

Style is the appear several fashionable people are targeting. Putting on and as well bracelets won't present you with the actual style you happen to be aspiring to realize it also offers you the customized feel in conjunction with every distinctive style. You'll be able to can certainly make your individual Pandora Bracelets while using appropriate supplies and the playfulness of the creativity. Beads, spacers including clips may be in addition to the bracelets use a a whole lot more attractive fashion.get morepandora jewelry||fashion jewelry||wedding car||hat club||jewelry box
nhl jerseys nhl jerseys
cheap nhl jerseys cheap nhl jerseys
hockey jerseys hockey jerseys
nhl hockey jerseys nhl hockey jerseys
chicago blackhawks jerseys chicago blackhawks jerseys
montreal canadiens jerseys montreal canadiens jerseys

March 23, 2011 | Unregistered Commenterwedding plan

Such a nice linkimg thanks...

March 24, 2011 | Unregistered CommenterVitacost
Editor Permission Required
You must have editing permission for this entry in order to post comments.