kuang
kuang

Reputation: 309

UITableView's deselectRowAtIndexPath prevents cell's subviews from changing background color

I am working on a tableview with custom cells. I need to highlight current selected (active) cell by setting background color for the title label, which is an immediate subview of cell.contentView. My code logic is like this (revised for better understanding):

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    UITableViewCell *previousActiveCell = [tableView cellForRowAtIndexPath:_indexPathActiveCell]; // previous selection
    UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath: indexPath]; // current selection

    UILabel *labelpreviousActiveCellTitle = [previousActiveCell.contentView viewWithTag:SUBVIEW_TAG_TITLE_LABEL];
    UILabel *labelSelectedCellTitle = [selectedCell.contentView viewWithTag:SUBVIEW_TAG_TITLE_LABEL];

    labelpreviousActiveCellTitle.backgroundColor = [UIColor clearColor]; // remove highlighting from previous selection
    labelSelectedCellTitle.backgroundColor = [UIColor redColor]; // highlighted

    _indexPathActiveCell = indexPath; // update _indexPathActiveCell with current selection
}

The problem is, when a new cell is selected, the highlighting background color appears for a very short moment, about half a second, and then disappears. If I comment out the calling for deselectRowAtIndexPath,

// [tableView deselectRowAtIndexPath:indexPath animated:YES];

the highlighting background color will retain. My guess is that deselectRowAtIndexPath remembers all the subviews' previous background color and when it recovers the cell from the shaded background, it changes all the subviews' background color back.

My workaround is adding a delay like this:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    labelpreviousActiveCellTitle.backgroundColor = [UIColor clearColor];
    labelSelectedCellTitle.backgroundColor = [UIColor redColor];
});

and it works. Note that I also tried a shorter delay like 0.01 second which did not work.

Setting the delay time with a magic number is an unpleasant way. My question is, is there any better way to set the background color for cell's subviews in tableview's didSelectRowAtIndexPath delegate method? Thanks in advance.

Upvotes: 0

Views: 144

Answers (2)

elk_cloner
elk_cloner

Reputation: 2149

Make a temporary variable which will hold the selected cell in your view controller.I am using a custom cell. It's not so important here though

var selectedCellIndex:IndexPath?

in your viewDidLoad initialize this like this.

selectedCellIndex = IndexPath.init(row: -1, section: 0)// first row starts with 0 you know.

in your cellForRowAtIndexPath function modify it like this.

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "chatCell", for: indexPath) as! sampleTableViewCell


    if indexPath.row == selectedCellIndex?.row {
        cell.label.backgroundColor = UIColor.green
    }
    else {
        cell.label.backgroundColor = UIColor.clear
    }       
    cell.selectionStyle = .none //For removing grey color while cell selection.
    return cell

}

change your didSelectRowAtIndexPath like this.

public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    selectedCellIndex = indexPath
    tableView.reloadRows(at: tableView.indexPathsForVisibleRows!, with: .none) //reload only visible cells. don't call reloadData it's expensive.

}

Here is the output:

enter image description here

Upvotes: 2

Have you tried creating a custom cell that inherits from UITableViewCell?

The custom cell would have a label as a public property so you can reference the label from your controller, without tagging.

Upvotes: 1

Related Questions