Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent highlighting on selected UITableViewCell

I have a UITableView in which I need to programmatically select a cell if the data model says that the cell represents the selected choice in a list of items. I do this when I'm configuring the UITableViewCell:

if (group == self.theCase.assignedGroup) {
    [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
    self.selectedIndexPath = indexPath;
} else {
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}

I am seeing very odd behavior with this. If the first row of the tableview is the one that should be selected, the cell doesn't highlight its background properly. However, if the second row is the one that should be selected, it works as it's supposed to (screenshots at the end).

UPDATE: It could have something to do with the fact that I am loading data for the table asynchronously, and while the data is loading I show a different kind of cell with a progress indicator in that first row. Here's the table view data source code that's handling this:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    if (self.hasMorePages) {
        return self.groups.count + 1;
    }
    return self.groups.count;
}

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

    if (indexPath.row < self.groups.count) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"DSAssignCell" forIndexPath:indexPath];
        [self configureCell:cell forRowAtIndexPath:indexPath];
    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:@"DSLoadingCell" forIndexPath:indexPath];
        [self configureLoadingCell:cell forRowAtIndexPath:indexPath];
    }

    return cell;
}

- (void)configureCell:(UITableViewCell *)cell
    forRowAtIndexPath:(NSIndexPath *)indexPath
{
    DSGroup *group = self.groups[indexPath.row];
    if (group == self.theCase.assignedGroup) {
        [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
        self.selectedIndexPath = indexPath;
    } else {
        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    }
    cell.textLabel.text = group.name;
    cell.tag = kDataCellTag;
}

- (void)configureLoadingCell:(UITableViewCell *)cell
           forRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIActivityIndicatorView *activityIndicator;
    if ([cell viewWithTag:kActivityIndicatorTag]) {
        activityIndicator = (UIActivityIndicatorView *)[cell viewWithTag:kActivityIndicatorTag];
    } else {
        activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        activityIndicator.center = cell.center;
        activityIndicator.tag = kActivityIndicatorTag;
        [cell.contentView addSubview:activityIndicator];
    }
    [activityIndicator startAnimating];
    cell.tag = kLoadingCellTag;
}

UPDATE As requested, here is the code that handles the asynchronous loading of the group & agent data from the web service:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self resetData];
    [self loadData];
}

- (void)resetData
{
    self.currentPage = 0;
    self.hasMorePages = YES;
    self.groups = [[NSMutableArray alloc] initWithCapacity:kGroupsPerPage];
    self.agents = [[NSMutableArray alloc] initWithCapacity:kAgentsPerPage];
}

- (void)loadData
{
    if (self.showingGroups) {
        [DSGroup fetchGroupsOnPage:self.currentPage + 1 perPage:kGroupsPerPage success:^(NSArray *groups, NSDictionary *links, NSNumber *totalEntries) {
            [self.groups addObjectsFromArray:groups];
            [self didLoadDataPage:(links[@"next"] != [NSNull null])];
        } failure:^(NSError *error) {
            [self showAlert:@"Could not load groups. Please try again later." withError:error];
        }];
    } else {
        [DSUser fetchUsersOnPage:self.currentPage + 1 perPage:kAgentsPerPage success:^(NSArray *users, NSDictionary *links, NSNumber *totalEntries) {
            [self.agents addObjectsFromArray:users];
            [self didLoadDataPage:(links[@"next"] != [NSNull null])];
        } failure:^(NSError *error) {
            [self showAlert:@"Could not load users. Please try again later." withError:error];
        }];
    }    
}

- (void)didLoadDataPage:(BOOL)hasMorePages
{
    self.hasMorePages = hasMorePages;
    self.currentPage++;
    [self.tableView reloadData];
}

Here's a screenshot of trying to select (and highlight) the first row, which is wrong (no gray background): Trying to select row 0

Here's a screenshot of trying to select (and highlight) the second row, which is correct: [Trying to select row 1

Any idea what might be going on here?

like image 489
Jamie Forrest Avatar asked Nov 10 '22 16:11

Jamie Forrest


1 Answers

I wasn't able to fix this using the built-in selection styles of UITableViewCell, but subclassing it and overriding setSelected:animated fixed it:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
     [super setSelected:selected animated:animated];

     self.contentView.backgroundColor = selected ? [UIColor grayColor] : [UIColor whiteColor];
}
like image 96
Jamie Forrest Avatar answered Nov 15 '22 08:11

Jamie Forrest