I have a UITableView with reorderable rows and I’m using the standard UITableViewCell.text property to display text. When I tap Edit, move a row, tap Done, then tap the row, the built-in UILabel turns completely white (text and background) and opaque, and the blue shade to the cell doesn’t show behind it. What gives? Is there something I should be doing that I’m not? I have a hacky fix, but I want the real McCoy.
Here is how to reproduce it:
Starting with the standard ‘Navigation-Based Application’ template in the iPhone OS 2.2.1 SDK:
-
Open RootViewController.m
-
Uncomment
viewDidLoad, and enable the Edit button:- (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. self.navigationItem.rightBarButtonItem = self.editButtonItem; } -
Specify that the table has a few cells:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 4; } -
In
tableView:cellForRowAtIndexPath:, add a line to set the text property of a cell, and therefore to use the built-in UILabel subview:// Set up the cell... cell.text = @'Test'; -
To enable reordering, uncomment
tableView:moveRowAtIndexPath:toIndexPath:. The default implementation is blank, which is fine in this case since the template doesn’t include a data model. -
Configure the project for the Simulator, OS 2.2.1, Build and Go. When the app comes up, tap Edit, then slide any row to a new position, tap Done, and then tap each row one at a time. Usually a tap will select a row, turn it blue, and turn its text white. But a tap on the row that you just moved does that and leaves the UILabel’s background color as white. The result is a confusing white open space with blue strips on the edges. Oddly enough, after the first bogus tap, another tap appears to correct the problem.
So far I have found a hack that fixes it, but I’m not happy with it. It works by ensuring that the built-in UILabel is non-opaque and that it has no background color, immediately upon selection.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // hacky bugfix: when a row is reordered and then selected, the UILabel displays all crappy UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; for (UIView *view in cell.contentView.subviews) { if ([[view class] isSubclassOfClass:[UILabel class]]) { ((UILabel *) view).backgroundColor = nil; view.opaque = NO; } } // regular stuff: only flash the selection, don't leave it blue forever [tableView deselectRowAtIndexPath:indexPath animated:YES]; }
This appears to work, but I don’t expect it to be a good idea forever. What is the Right Way to fix this?
This looks like a bug in UITableView’s rendering, and you should file a Radar bug report on it. It’s like the cells don’t get refreshed properly after the move.
One way to work around this for now is to not use the built-in label, but roll your own in the cell:
I tried this, and it doesn’t exhibit the same sort of white blank rectangle you see with the built-in label. However, adding another non-opaque view to the table cell might not be the best for overall rendering performance.
I don’t know how major of a glitch this is, because Apple doesn’t want you to persist a selection highlight on a table row (they’ve been enforcing this lately during the review process). You’re supposed to place a checkmark or move on to the next level in the navigation hierarchy with a selection, at which point this white box would only be on the screen for a fraction of a second.