5

I have a very simple UITableViewController subclass designed to show users the characters in the alphabet in cells. When the user presses on a cell, it is to set its accessory type to a checkmark.

#import "MTGTableViewController.h"

@interface MTGTableViewController ()

@property (nonatomic, strong) NSArray *data;

@end

@implementation MTGTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    _data = @[@"A", @"B", @"C", @"D", @"E", @"F", @"G", @"H", @"I", @"J", @"K", @"L", @"M", @"N", @"O", @"P", @"Q", @"R", @"S", @"T", @"U", @"V", @"W", @"X", @"Y", @"Z"];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _data.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" forIndexPath:indexPath];

    // Configure the cell...
    cell.textLabel.text = _data[indexPath.row];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}

@end

The table view works fine. I have my reuseIdentifier property set in the storyboard on my prototype cells, and it all looks good before I start selecting cells.

The problem: When I select any cell, say the "A" cell, other not-yet-visible cells are also given checkmarks when i scroll down to them. Even worse, when I scroll up and down, sometimes the checkmark on cell "A" is removed and given to cell "B".

michaelsnowden
  • 6,031
  • 2
  • 38
  • 83

4 Answers4

6

This is because of the way table views reuse cells. You need to make sure to clear the accessory item after you call dequeueReusableCellWithIdentifier. eg:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" forIndexPath:indexPath];

    // Configure the cell...
    cell.textLabel.text = _data[indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryNone;

    return cell;
}

Although--you're going to have a different problem after you make this change. The cell is going to "forget" that it is selected because of this code. So you'll need to actually change that line where the accessoryType is set to check and see if the cell is selected or not.

Nicholas Hart
  • 1,734
  • 13
  • 22
3

I had this problem a little while ago as well, you need to add another array that keeps track of the which cell are marked. Just make an array of NSIndexPaths that are the ones marked. Sort of like:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
     UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
     if ([arrayOfMarked containsObject:indexPath]) {
         cell.accessoryType = UITableViewCellAccessoryNone;
     } else {
         cell.accessoryType = UITableViewCellAccessoryCheckmark;
     }
}
David Cao
  • 525
  • 3
  • 11
  • Damn, that's pretty sucky. But this is what I ended up doing. – michaelsnowden Jul 15 '14 at 23:19
  • @michaelsnowden this is not the best way to go. I asked a similar question, and turns out that you'd need to provide a "default" state for your cells, since they're being reused. (@Nicolas Hart below, is actually suggesting that) Here's the link to my question; http://stackoverflow.com/questions/39198461/uitableviewcell-reusability-issue-modifying-one-cell-is-affecting-others – Priest Sep 23 '16 at 22:45
1

For Swift 3:

A simplified version I discovered was to use swifts ".indexPathsForSelectedRows" just within the "cellForRowAt" function.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell

    //To keep from reusing checkmark in reusableCell
    if let selectedIndexPaths = tableView.indexPathsForSelectedRows {
        if selectedIndexPaths.contains(indexPath) {
            cell.accessoryType = .checkmark
        } else {
            cell.accessoryType = .none
        }
    }

    cell.contactsOutlet.text = namesOrganized[indexPath.section].names[indexPath.row]
    return cell
}

Then in didSelect and didDeselect:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let cell = tableView.cellForRow(at: indexPath)
    cell?.accessoryType = UITableViewCellAccessoryType.checkmark
}

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

    let cell = tableView.cellForRow(at: indexPath)
    cell?.accessoryType = UITableViewCellAccessoryType.none
}
JHaley2525
  • 11
  • 4
0

on didselectRowAtIndex path, append the index path into an array of indexpath

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


    if selectedIndexPaths.contains(indexPath) {

        let indexOfIndexPath = selectedIndexPaths.indexOf(indexPath)!
        selectedIndexPaths.removeAtIndex(indexOfIndexPath)
    } else {
        selectedIndexPaths += [indexPath]
    }

    tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)

}

and then on the cellForRowAtIndexPath use the selectedIndexpaths Array to check if the cell is selected or not

 override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let mycell = tableView.dequeueReusableCellWithIdentifier("yourCell", forIndexPath: indexPath) as! YourCustomCell
 .
 .
 .
    if selectedIndexPaths.contains(indexPath){
 // do what you want to do if the cell is selected


    } else {

 // do what you want to do if the cell is not selected
    }
Led
  • 662
  • 1
  • 19
  • 41