UITableView selected cell doesn't stay selected when scrolled

I’m having problems with table view cells not keeping their “selected” state when scrolling the table. Here is the relevant code:

@property (nonatomic, strong) NSIndexPath *selectedIndexPath;

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    self.selectedIndexPath = indexPath;
    //do other stuff
}

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

    MyCustomCell_iPhone* cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell_iPhone"];

    if (cell == nil)
        cell = [[[NSBundle mainBundle] loadNibNamed:@"MyCustomCell_iPhone" owner:self options:nil] objectAtIndex:0];

    if ([indexPath compare: self.selectedIndexPath] == NSOrderedSame) {
        [cell setSelected:YES animated:NO];
    }

    return cell;
}

And for the cell:

  • viewForHeaderInSection creating a memory leak?
  • Lazy load images for UITableViewCells from NSDocumentDirectory?
  • UITextView and cell dynamically update height based on content of textview?
  • Pass custom class data between view controllers in Swift
  • Subclassing UITableViewCell correctly?
  • Expanding and collapsing tableView sections iOS?
  • - (void)setSelected:(BOOL)selected animated:(BOOL)animated
    {
        [super setSelected:selected animated:animated];
    
        if (selected) {
            self.selectedBg.hidden = NO;
        }else{
            self.selectedBg.hidden = YES;
        }
    }
    
    - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
    {
        [super setHighlighted:highlighted animated:animated];
    
        if (highlighted) {
            self.selectedBg.hidden = NO;
        }else{
            self.selectedBg.hidden = YES;
        }
    }
    

    How can I get the selected cell to stay highlighted? If I scroll it off the screen, when it scrolls back on the screen it appears in its unselected state (with its selectedBg hidden).

    EDIT:
    Removing the setHighlighted method from the cell fixes the issue. However that means that I get no highlighted state when pressing the table cell. I’d like to know the solution to this.

    9 Solutions Collect From Internet About “UITableView selected cell doesn't stay selected when scrolled”

    Had the same problem, selected cell’s accessoryView disappeared on scroll. My co-worker found pretty hack for this issue. The reason is that in iOS 7 on touchesBegan event UITableView deselects selected cell and selects touched down cell. In iOS 6 it doesnt happen and on scroll selected cell stays selected. To get same behaviour in iOS 7 try:

    1) Enable multiple selection in your tableView.

    2) Go to tableView delegate method didSelectRowAtIndexPath, and deselect cell touched down with code :

       NSArray *selectedRows = [tableView indexPathsForSelectedRows];
    for(NSIndexPath *i in selectedRows)
    {
        if(![i isEqual:indexPath])
        {
            [tableView deselectRowAtIndexPath:i animated:NO];
        }
    }
    

    Fixed my problem! Hope it would be helpful, sorry for my poor English btw.

    I know my method is not very orthodox but seems to work. Here is my solution:

    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if cell.selected {
            cell.selected = true
        } else {
            cell.selected = false
        }
    }
    

    You must implement all the methods you mentioned on your post as well (@soleil)

    iOS 7/8 both deselect the cell when scrolling begins (as Alexander Larionov pointed out).

    A simpler solution for me was to implement this UIScrollViewDelegate method in my ViewController:

     - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
         NSInteger    theRow       = [self currentRowIndex]; // my own method
         NSIndexPath *theIndexPath = [NSIndexPath indexPathForRow:theRow inSection:0];
         [self.myTableView selectRowAtIndexPath:theIndexPath 
                                       animated:NO 
                                 scrollPosition:UITableViewScrollPositionNone];
     }
    

    This works because my viewController is the UITableView’s delegate, and UITableView inherits from UIScrollView.

    If you want to achieve the same thing in Swift then here is the code. By the way I am using Xcode 7.2 with Swift 2.1.

    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if cell.selected == true{
            cell.selected = true
            cell.backgroundColor = UIColor.blackColor()
        }else{
            cell.backgroundColor = tableViewCellColor //Don't panic its my own custom color created for the table cells.
            cell.selected = false
        }
    }
    

    Do other customization what ever you want..

    Thanks..

    Hope this helped.

    Have you tried comparing the rows of the index paths instead of the entire index path object?

    if ((indexPath.row == self.selectedIndexPath.row) && (indexPath.section == self.selectedIndexPath.section)) {
        [cell setSelected:YES animated:NO];
    }
    

    Here’s the solution I came up with — and it doesn’t even feel hacky.

    1) Implement -scrollViewWillBeginDragging: and -scrollViewWillEndDragging:withVelocity:targetContentOffset: and manually highlight the cell for the selected row (if there is one) during scrolling.

    Mine look like this:

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollview {
        self.scrollViewIsDragging = YES;
    
        if( [self.tableView indexPathForSelectedRow] ) {
            [[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:YES];
        }
    }
    
    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
        self.scrollViewIsDragging = NO;
    
        if( [self.tableView indexPathForSelectedRow] ) {
            [[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:NO];
        }
    }
    

    The scrollViewIsDragging property is there so that in -tableView:cellForRowAtIndexPath: we can make sure any newly dequeued cells have the proper highlighting (e.g. if the cell for the selected row is scrolled onto screen after having been off screen). The pertinent part of that method looks like this:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        // ... cell creation/configuration ...
    
        if( self.scrollViewIsDragging && [[tableView indexPathForSelectedRow] isEqual:indexPath]) {
            [cell setHighlighted:YES animated:NO];
        }
    

    }

    …and there you have it. The cell for the selectedRow will stay highlighted during scrolling.

    UITableViewCell has a BOOL property “selected“. Whenever you load the cell, check the state of selected and make selection or deselection accordingly as follows in cellForRowAtIndexPath definition:

    if (cell.selected) {
        // Maintain selected state
    }
    else{
         // Maintain deselected state
    }
    

    Posted a quick answer to that here:
    https://stackoverflow.com/a/35605984/3754003

    In it, I also explain why this happens.

    Swift 3 solution, 2017.

    I fixed the problem with this simple line of code:

    cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false
    

    Inside the tableView(tableView:cellForRowAt indexPath:) method:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        // Dequeue a reusable cell
        if let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID") {
    
            cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false
    
            // Now you can safely use cell.isSelected to configure the cell 
    
            // ...your configurations here
    
            return cell
        }
    
        return UITableViewCell()
    }