✔ Checkmark selected row in UITableViewCell

I am an iOS development newbie. I want to add a checkmark to my UITableViewCell when it is selected. The checkmark should be removed when another row is selected. How would I do this?

Do not use [tableview reloadData]; // its a hammer.

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

-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}

In your UITableViewDatasource method:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(cell == nil )
    {
        cell =[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }
    if ([indexPath compare:self.lastIndexPath] == NSOrderedSame) 
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } 
    else 
    {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    return cell;
}

// UITableView Delegate Method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    self.lastIndexPath = indexPath;

    [tableView reloadData];
}

And lastIndexPath is a property(strong) NSIndexPath* lastIndexPath;

I found that reloading the data interrupts the deselect animation in an ugly way.

This Swift implementation cleanly adds/removes checkmarks and deselects the row:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if self.lastSelection != nil {
        self.myTableView.cellForRowAtIndexPath(self.lastSelection)?.accessoryType = .None
    }

    self.myTableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark

    self.lastSelection = indexPath

    self.myTableView.deselectRowAtIndexPath(indexPath, animated: true)
}

where lastSelection is declared as var lastSelection: NSIndexPath!

No extra activity in cellForRowAtIndexPath needed. Shouldn't be hard to replicate in Obj-C.

To set a checkmark:

UITableViewCell *cell = ...;
cell.accessoryType = UITableViewCellAccessoryCheckmark;

To select/deselect a cell:

[cell setSelected:TRUE animated:TRUE]; // select
[cell setSelected:FALSE animated:TRUE]; // deselect

To deselect the previous cell use a NSIndexPath *lastSelected ivar to track the last selected cell:

- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
   if (self.lastSelected==indexPath) return; // nothing to do

   // deselect old
   UITableViewCell *old = [self.tableView cellForRowAtIndexPath:self.lastSelected];
   old.accessoryType = UITableViewCellAccessoryNone;
   [old setSelected:FALSE animated:TRUE];

   // select new
   UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
   cell.accessoryType = UITableViewCellAccessoryCheckmark;
   [cell setSelected:TRUE animated:TRUE];

   // keep track of the last selected cell
   self.lastSelected = indexPath;
}

extension ViewController : UITableViewDelegate,UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.dataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = dataArray[indexPath.row]
        if selectedData.contains(dataArray[indexPath.row]) {
            cell.accessoryType = .checkmark
        }else{
            cell.accessoryType = .none
        }
        return cell
    }


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

        if selectedData.contains(dataArray[indexPath.row]) {
            selectedData.removeLast()
            tableView.cellForRow(at: indexPath)?.accessoryType = .none
        }else {
            selectedData.removeAll()
            selectedData.append(dataArray[indexPath.row])
            tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
        }
        print(selectedData)
    }

    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        tableView.cellForRow(at: indexPath)?.accessoryType = .none
    }

}

based on dataArray table view formed.. similarly, I took an empty array, and whenever the user taps on a cell, based on indexValue of from dataArray I stored that object in selectedDataArray

As for the question its like... A question has multiple options(Answers), But at finally only one or none answer will be a result

Likewise, only one cell should show checkmark and remaining cells should be in unselected... it some case u can deselect your answer... I hope this is the best answer to this question

Comments
  • What if I want only the checkmark and want to deselect the row after a selection?
  • it selecting same index row in every section @Ujwal Manjunath
  • -(void)reloadData should be relatively cheap, according to the docs. Nevertheless, this is more semantically pleasing.
  • Why shouldn't you use '[tableview reloadData]'? I want to use multiselect in my tableview. I am using the the objects received from the server to set the checkmark in my tableview cell. Once, I do that, I save the check objects in a global array. However, when I use that array to compare inside my 'didSelectRowAtIndexPath' method to see if the indexpath matches to this object inside my global array. I am not able to find it. I think its the [tableView reloadData]. What do you think?Please advice.
  • The above answers not work if you reused the cell for large number of data. On scroll you can see repeated checkmark. Check my solution stackoverflow.com/questions/7982944/…
  • This first line ist still not formated as code. Use interpunctation!
  • This Answer is Criminally underrated.
  • Thanks @WaaleedKhan