views:

3598

answers:

4

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:

  1. Open RootViewController.m

  2. 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;
    }
    
  3. Specify that the table has a few cells:

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return 4;
    }
    
  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";
    
  5. 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.

  6. 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?

+6  A: 

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:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];

     CGRect frame = cell.contentView.bounds;
     frame.origin.x = frame.origin.x + 10.0f;

     UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
     [textLabel setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
     textLabel.tag = 1;
     textLabel.textAlignment = UITextAlignmentLeft;
     textLabel.backgroundColor = [UIColor clearColor];
     textLabel.textColor = [UIColor blackColor];
     textLabel.font = [UIFont boldSystemFontOfSize:20.0];
     textLabel.numberOfLines = 1;
     textLabel.highlightedTextColor = [UIColor whiteColor];
     [cell.contentView addSubview:textLabel];
     [textLabel release];

    }

    UILabel *textLabel = (UILabel *)[cell viewWithTag:1];
    textLabel.text = @"Test";

    return 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.

Brad Larson
+1 on the selection note. Selections aren't meant to be shown for extended periods! (We got knocked on this for several of our apps)
Ed Marty
I concur; we just rev'd Crosswords to do away with using the highlighted row to indicate persistent selection
Ben Gottlieb
+1  A: 

The trick in the solution from Brad appears to be:

textLabel.backgroundColor = [UIColor clearColor];

If you leave the background as the default you still get the problem even when you roll your own cells UITableViewCells. The reason I left it as the default is because the documentation says it is less computationally costly to use opaque backgrounds. Ideally I wouldn't want to use [UIColor clearColor] to fix this bug.

Maybe a completely custom painted cell would somehow fix it. I haven't tried those before though.

Does anyone else have a solution for this?

Cal
A: 

Thanks for the info, I was searching how to erase the background color from a UILabel.

I used the following line:

textLabel.backgroundColor = [UIColor clearColor];

and worked perfectly!!!

thanks

Alejandra :)

Alejandra
A: 

Selections aren't meant to be shown for extended periods! (We got knocked on this for several of our apps)

??? That means Apple would not approve their own Calendar app on iPhone! When you go to edit the start and end times of the event, the start time is selected indefinitely, it only changes once the user taps to the next field.

bparanj
not an answer but a comment
Till