Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After moving a UITableView row, then selecting it, it only turns blue at the edges

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?

like image 464
easeout Avatar asked Mar 01 '23 23:03

easeout


2 Answers

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.

like image 153
Brad Larson Avatar answered Apr 16 '23 08:04

Brad Larson


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?

like image 33
Cal Avatar answered Apr 16 '23 08:04

Cal