Wanner asks:
“How can I make my cells alternate colors across multiple sections if the sections don’t always have the same number of rows?”
A nice effect that you sometimes see in table views like the app store app is when you alternate the tableview cell backgrounds between light and dark shades of a color.
Regular tableView cells would be set up like this. This example is from the template for a navigation-based app, I only added the row where the cells label is set.
// Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } // Configure the cell. cell.textLabel.text = @"Row"; return cell; } |
One might be tempted to also set the cell’s background in here but you will get confusing results if you do. The iPhoneOS does some tricky stuff creation of the cell and actually displaying it to maximize performance but still allow for a blue selection behind the cell’s content without you having to think about how these cells are composited.
From a WWDC video I learned that the best place to put such alternating is not in the place you would normally use to set up a cell. There is a method that gets called right before the drawing and this gives you the opportunity to do some “last millisecond changes” before the cell actually draws itself in it’s place.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { cell.backgroundColor = (indexPath.row%2)?[UIColor lightGrayColor]:[UIColor grayColor]; } |
Note the C-ish way to make the color alternate. % is the modulo operator. And ?: is a shorthand for an if. condition?true-value:false-value. This is called when the cell “will be displayed” as you can see from the name.
Now the row numbers start from zero for every section. So we have to find a way to get the real row number for the whole table view. The only way I know of is to actually do a bit of counting ourselves. But since there are already delegate methods to return the number of sections and the number of rows for any given section this is fairly straightforward.
Here’s the whole code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 4; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 3; } - (NSInteger)realRowNumberForIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)tableView { NSInteger retInt = 0; if (!indexPath.section) { return indexPath.row; } for (int i=0; i<indexPath.section;i++) { retInt += [tableView numberOfRowsInSection:i]; } return retInt + indexPath.row; } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger realRow = [self realRowNumberForIndexPath:indexPath inTableView:tableView]; cell.backgroundColor = (realRow%2)?[UIColor lightGrayColor]:[UIColor grayColor]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [NSString stringWithFormat:@"Section %d", section]; } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } // Configure the cell. cell.textLabel.text = @"Row"; return cell; } |
It’s smart to use what you got and nobody can tell you not to use your own tableView datasource methods to calculate a needed value on the fly.
Categories: Q&A