Common Cocoa Coding Patterns: UITableView row index enum

By uliwitness

One of the things pretty much every iPhone application has is a table view containing a pre-defined list of main menu items that lead to deeper sections. Many people just hard-code this list and the item indexes, only to later find out that they need to display some rows conditionally. The code becomes a mess of conditional statements duplicated all over the place to adjust the indexes in the case that a certain item does not apply.

But there is an easier way. The first thing you do, is create an enum with the item indexes:

enum
{
    kMainTableRowInbox = 0,
    kMainTableRowTrash,
    kMainTableRowSpam,
    kMainTableNumberOfRows
};

Whenever you would use the indexes, e.g. in your table view data source and delegate methods, use these constants instead. You have the same code as before, but if you want to add/insert a new item, you can just add the constant for it in the right spot, and the rest of your code magically updates itself.

If you have one source file that implements the main list for several similar products, you can even use the preprocessor to remove items that are only needed for one device. However, what do you do if you dynamically need to add/remove items at runtime? E.g. if your application can talk to two kinds of servers, and one has a kMainTableRowTrash, but the other kind doesn’t? Simple: You create a look-up-table:

static NSUInteger          sRowLookUpTable[kMainTableNumberOfRows] = { 0 };
static NSUInteger   sRowLookUpTableSize = 0;

-(void) rebuildRowLookUpTable
{
    NSUInteger  actualRowNumber = 0;
    for( NSUInteger virtualRowNumber = 0; virtualRowNumber < kMainTableNumberOfRows; virtualRowNumber++ )
    {
        if( virtualRowNumber == kMainTableRowTrash && ![self haveTrash] )
            continue;
        sRowLookUpTable[actualRowNumber] = virtualRowNumber;
        actualRowNumber++;
    }

    sRowLookUpTableSize = actualRowNumber;
}

Call this in your init method(s), or whenever some state changes that may affect which rows should be shown. Change your table view delegate methods to use sRowLookUpTableSize instead of kMainTableNumberOfRows for the number of rows. And whenever you look at an index to compare it to one of the row index constants, do

sRowLookUpTable[indexPath.row]

instead of just

indexPath.row

to translate the real row index into one of the constants that indicate what content you want displayed. This way, most of your code never needs to know in which order which items are actually shown in your table. It just needs to be able to display all kinds of rows, and only -rebuildRowLookUpTable needs to actually know which rows are hidden or visible.

Also, the constant names make it obvious what row a bit of code is dealing with. If, two years after you wrote an app on contract, the customer comes back and wants it updated for MacBook Airs with iOS, and remove the settings category, you can just search for a kMainTableRowSettings constant in the text editor and get rid of all that code. Or just move the constant below the kMainTableNumberOfRows constant. All the code is still there, compiles, but the index will never be added to the table.

2 Comments Leave a comment

  1. What is the need to making the sRowLookUpTable and sRowLookUpTableSize static ?

  2. static variables are like global variables, but are “invisible” to anyone outside the current file (or current function/method, if you declare it inside one). Doing it this way avoids conflicts when using this code in several files. Each file gets its own arrays. Otherwise you’d get either an error message that there are two globals with the same name, or it’ll quietly use one array for both tables, leading to weird behaviour.

    An alternative approach (especially if this is really a controller that lists a server’s contents) would be to make them instance variables. But if you only have one main table or the categories don’t change after the app launches, you can go for statics instead.

    Static variables are a feature inherited from C. So you can probably find a more detailed explanation in C tutorials.

Share your thoughts