Some favorite site feeds aggregated locally: iPhone Development RSS   Adobe Labs RSS   Macrumors RSS

Tuesday, May 12, 2009

iPhone: UITableView customization

Tuesday, May 12, 2009    1 Comments

I have a UITableView where I am providing my own graphics for the normal and selected states. Because my graphic for the selected state is dark, the dark text in my UILabels in each cell needs to turn to white for legibility. Then of course it needs to be reset afterwards to black on the normal graphic.

After spending a lot of time on forums, Google, and email lists, I have a solution that should have been easier to get to. I received suggestions that I keep a pointer around for the previously selected item, so when a selection is detected I could introspect the current cell, change the text color there, and then change the previous cell's UILabels back to normal. A lot of work, and it's keeping state which is a bad idea. There were mentions of reloadData for the table, etc.

However one thing I overlooked initially was that I was thinking of the table cell as retaining it's selected state. Normally you'd just call up another view from a selection in a table. So I didn't really need to maintain the selected visual state in the table, but I needed to show it to the user.

A fine chap from Apple emailed me that most iPhone applications implement a nice animation for selected cells in a table and that I should follow that paradigm. So that got me digging.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//NSLog(@"%d:%d", [indexPath row], [indexPath section] );
// Need to get to the cell
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *cellLabel = (UILabel *)[newCell.contentView viewWithTag:1];
NSLog( @"%@", [cellLabel text] );
}
That bit of animation basically tells the table to deselect the selected item with animation. This works great but it doesn't reset the text color in the cell itself. That's when I stumbled onto the holy grail solution for me being able to reset the text without having to dig into the cell itself or keep a pointer around for the previous selected cell. It was simple using a property I hadn't run into yet before so I didn't know of it's existence... highlightedTextColor. DOH! That was it!

So pairing the above method with this detail in my cellForRowAtIndexPath, I get exactly what I needed:
...
labelThree = [[[UILabel alloc] initWithFrame:CGRectMake( 65, 34, 200, 25)] autorelease];
labelThree.tag = 3;
labelThree.font = [UIFont systemFontOfSize:10.0];
labelThree.backgroundColor = [UIColor clearColor];
labelThree.textColor = UIColorFromRGB(0x555555);
labelThree.highlightedTextColor = [UIColor whiteColor]; // Why didn't I know about this!!!
labelThree.text = [NSString stringWithFormat:@"%@", [[photoArray objectAtIndex:row] objectForKey:@"rating"]];
...
I set the text color (using a macro) and also the highlighted text color. So what happens is that the background transitions to my dark selected graphic while the text turns white, and upon deselection the text returns back to that 0x555555 color while the dark selected graphic fades away leaving the normal graphic for the cell.

This works perfectly without me keeping state myself anywhere... let the APIs handle all of that crap for me. I burned a whole day on this simple thing, but it takes moments like these to really understand and learn (in my opinion).

Labels: , ,

 

Wednesday, May 6, 2009

Getting columned data in a UITableView

Wednesday, May 6, 2009    0 Comments

Since I'm still fairly green to this whole Objective-C thing and I am still learning the APIs that are available (there are a billion of them), I don't always know how to do exactly what I want to do. A case in point... I wanted to present a high scores screen in a little game I am coding up (a learning exercise... I highly recommend coding a game to expose yourself to tons of little challenges).

I have an NSMutableArray that I have stuffed with NSDictionary items. Each of those has a key for name and score. Easy. However I thought about using the "\t" character like we can do in ActionScript 3 and implement a tabStop somehow... thus negating the need to use dictionaries at all and just munge the name with the score.

Obviously this makes sorting an extremely stupid activity (many hoops to needlessly jump through). So it's not a solution by any means (an array of dictionaries is), but I wanted to see how I might replicate the functionality of tabStops in Objective-C (non-paragraph).

I have yet to find the solution. However in beating myself up in trying to figure out how to properly implement NSTextTab for a non UITextField I came across a true solution. Adding subviews!

This is exactly what I was after, and it has the benefit of allowing tons of flexibility in how to best present your textual data in the UITableView.

Without dressing the main method up too much, I think you'll be able to see what's going on here. I am only using a straight NSArray for this example, but you can see how this works.
// Set up the table view cells
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];

UILabel *label; // for scores
UILabel *nameLabel;

if( cell == nil ){

CGRect frame = CGRectMake(0,0,300,44);
cell = [[[UITableViewCell alloc] initWithFrame:frame reuseIdentifier:SimpleTableIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;

//Create the score label in the cell
label = [[[UILabel alloc] initWithFrame:CGRectMake(0.0, 10.0, 70.0, 25.0)] autorelease];
label.tag = 1;
label.font = [UIFont boldSystemFontOfSize:18.0];//systemFontOfSize
label.textAlignment = UITextAlignmentRight;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
label.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:label];
label.text = @"1000";

// Create the name label in the cell
nameLabel = [[[UILabel alloc] initWithFrame:CGRectMake(80.0, 10.0, 195.0, 25.0)] autorelease];
nameLabel.tag = 2;
nameLabel.font = [UIFont boldSystemFontOfSize:18.0];
nameLabel.textColor = [UIColor whiteColor];
nameLabel.backgroundColor = [UIColor clearColor];
nameLabel.lineBreakMode = UILineBreakModeWordWrap;
nameLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:nameLabel];

} else {
// Out of view items
label = (UILabel *)[cell.contentView viewWithTag:1];
nameLabel = (UILabel *)[cell.contentView viewWithTag:2];
}

NSInteger row = [indexPath row];
nameLabel.text = [listData objectAtIndex:row];
cell.selected = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}

Labels: , , , ,