Common Cocoa Coding Patterns: UITableView row index enum
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.