29

I am confused a little bit about settings table view cell accessories.

I have fixed two sections in my table

  • Home
  • Office

What I want is as follow....

  • When User tap any of the cell
  • Cell gets selected &
    • I want to set checked (set uitableviewcell accessory type -checked of tapped cell )
  • And also all other cell's accessory type now should set to
    • uitable view cell accessory type none

I have tried following code. But I found that indexpath.row & indexpath.section is readonly properties.

// Override to support row selection in the table view.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {    
    [tblProfileView deselectRowAtIndexPath:indexPath animated:YES];
    int i,max;
    UITableViewCell *x; NSIndexPath *tt;

    for(i=0;i<[tblProfileView numberOfRowsInSection:0];i++)
    {
        tt.row=i; tt.section=0;
        x=[tblProfileView cellForRowAtIndexPath:];
        [x setAccessoryType:UITableViewCellAccessoryNone];
    }
    for(i=0;i<[tblProfileView numberOfRowsInSection:1];i++)
    {
        tt.row=i; tt.section=1;
        x=[tblProfileView cellForRowAtIndexPath:tt];
        [x setAccessoryType:UITableViewCellAccessoryNone];
    }

    x=[tblProfileView cellForRowAtIndexPath:indexPath];
    [x setAccessoryType:UITableViewCellAccessoryCheckmark];
    // Navigation logic may go here -- for example, create and push another view controller.
    // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
    // [self.navigationController pushViewController:anotherViewController animated:YES];
    // [anotherViewController release];
}
sagarkothari
  • 24,520
  • 50
  • 165
  • 235

9 Answers9

52

I would keep track of the data that should be checked and change the cell in tableView:didSelectRowAtIndexPath: and update which data is checked in tableView:cellForRowAtIndexPath: like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // do usual stuff here including getting the cell

    // determine the data from the IndexPath.row

    if (data == self.checkedData)
    {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // determine the selected data from the IndexPath.row

    if (data != self.checkedData) {
       self.checkedData = data;
    }

    [tableView reloadData];
}
gerry3
  • 21,420
  • 9
  • 66
  • 74
  • 1
    This is not the right away of doing it, since you call reloadData (which can be expensive depending on your data). You can simply to : let cell = tableView.cellForRowAtIndexPath(indexPath) cell?.accessoryType = (yourTest) ? .Checkmark : .None – maxday Mar 21 '16 at 12:44
  • You actually have to do almost both. Do the update directly as Max pointed out and configure the cell in cellForRowAtIndexPath or you cell states get mixed up when you scroll your list. – marsbear Aug 18 '16 at 18:49
  • 1
    Oh and implement didDeselectRowAtIndexPath as well so you can uncheck cells again. – marsbear Aug 18 '16 at 18:51
17
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];

    if (newCell.accessoryType == UITableViewCellAccessoryNone) {
        newCell.accessoryType = UITableViewCellAccessoryCheckmark;
    }else {
        newCell.accessoryType = UITableViewCellAccessoryNone;
    }


}

also you need to remove the checkmark accessory on cellForRowAtIndexPath

if ([selectedOptionsArray indexOfObject:cell.textLabel.text] != NSNotFound) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
    cell.accessoryType = UITableViewCellAccessoryNone;
}
palaniraja
  • 10,432
  • 5
  • 43
  • 76
  • 1
    This is different to what the OP asked for. There was no mention of allowing multiple or zero selections. When one turns on the other(s) must turn off. You solution lets the user toggle one row on and off by itself, and presumably that selectedOptionsArray stores multiple selected options rather than just one as requested. – Craig Dec 13 '11 at 03:56
  • 1
    @craig Yes, you are spot on. But I was using this code to toggle options (rows) for my app. I thought it could be useful, the snippet somewhat based on gerry3, but I didn't use [tableview reloaddata]. – palaniraja Dec 13 '11 at 09:44
  • Thanks @palaniraja I came across this post looking for your solution, so I could use the table list for selecting items. – robmcm Mar 01 '12 at 13:00
  • Very good trick. But i have just one problem with this. I have to select row twice to deselect it. Any guess why??? – Uniruddh Oct 08 '13 at 08:50
16

Declare one int variable named prev and implement this method:-

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

 UITableViewCell *cell =[tableView cellForRowAtIndexPath:indexPath];

   if (cell.accessoryType==UITableViewCellAccessoryNone) 
   {
      cell.accessoryType=UITableViewCellAccessoryCheckmark;
      if (prev!=indexPath.row) {
         cell=[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:prev inSection:0]];
         cell.accessoryType=UITableViewCellAccessoryNone;
         prev=indexPath.row;
     }

 }
 else{
     cell.accessoryType=UITableViewCellAccessoryNone;
 }
}
Rajat
  • 1,043
  • 12
  • 23
iKushal
  • 2,689
  • 24
  • 20
7

A shortest and easiest way to implement logic for checkmark in selected row only.....


1.Create a global object of NSIndexPath..

selectedIndexPathForCheckMark= [[NSIndexPath alloc] init];

2.In didSelectRowAtIndexPath..

//Assiging selected indexPath to the declared object

selectedIndexPathForCheckMark = indexPath;  
[tableView reloadData];

3.in cellForRowAtIndexPath..

if ([selectedIndexPathForCheckMark isEqual:indexPath]) {
    [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}else {
    //setAccessoryType None if not get the selected indexPath
    [cell setAccessoryType:UITableViewCellAccessoryNone];
}
Prasanna
  • 945
  • 10
  • 24
1

Here's how I do it with static cells, and bypassing cellForRow

Create a var that stores the location of the "checked" cell.

 NSInteger _checkedCell;

Then just implement willDisplayCell and didSelectRow methods like this.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (_checkedCell == indexPath.row) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    _checkedCell = indexPath.row;
    [self.tableView reloadData];
}
Beau Nouvelle
  • 6,962
  • 3
  • 39
  • 54
1

I known this question is quite old, but here is the Swift version based on Blackberry's response (which I've voted up by the way)

import UIKit

class PlacesViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {

    var placesArray = NSMutableArray()
    
    var previousCheckedIndex = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.placesArray.addObject("Tandil")
        self.placesArray.addObject("Balcarce")
        self.placesArray.addObject("Mar del Plata")
        
        self.tableView.rowHeight = UITableViewAutomaticDimension
    }

    
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.placesArray.count
    }
    
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        var cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell

        cell.textLabel?.text = self.placesArray.objectAtIndex(indexPath.row) as? String
        

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

        if (indexPath.row != previousCheckedIndex) {
            var cell: UITableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)!
            if (cell.accessoryType == UITableViewCellAccessoryType.None) {
                cell.accessoryType = UITableViewCellAccessoryType.Checkmark
                if (previousCheckedIndex != indexPath.row) {
                    cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: previousCheckedIndex, inSection: 0))!
                    cell.accessoryType = UITableViewCellAccessoryType.None
                    previousCheckedIndex = indexPath.row
                }
            } else {
                cell.accessoryType = UITableViewCellAccessoryType.None
            }
        
            tableView.reloadData()
        }
    }
}
Nacho Mezzadra
  • 886
  • 2
  • 12
  • 14
0

In Swift 2.0 using custom accessoryView uitableviewcell with swift

1º Creating Globar var IndexPath

var indexSelected = NSIndexPath()

2º In didSelectRowAtIndexPath

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        indexSelected = indexPath
        TB_Lojas.reloadData()
    }

3º in cellForRowAtIndexPath:

if SelectedIndexPath == indexPath {
                let img = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
                img.image = UIImage(named: "check")
                cell.accessoryView = img
            }else{
                let img = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
                img.image = UIImage(named: "uncheck")
                cell.accessoryView = img
            }
}
Pablo Ruan
  • 1,681
  • 1
  • 17
  • 12
0

Considering you only want to maintain a single selection, and if you have a custom UITableViewCell, I would recommend the following.

In your UITableViewController

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    //... 
    let data = dataArray[indexPath.row]
    if data.isSelected {
        tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
    }
    //...

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let data = dataArray[indexPath.row]
    data.isSelected = true
    let cell = tableView.cellForRowAtIndexPath(indexPath)
    cell?.setSelected(true, animated: true)
}

override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
    let data = dataArray[indexPath.row]
    data.isSelected = false
    let cell = tableView.cellForRowAtIndexPath(indexPath)
    cell?.setSelected(false, animated: true)
}

In the custom UITableViewCell

override func setSelected(selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
    self.accessoryType = .None
    if selected {
        self.accessoryType = .Checkmark
    }
}

As long as allowsMultipleSelection is not enabled, it will automatically handle deselecting other cells when a new one is selected. If allowMultipleSelection is enabled, then it will work as well, except you will just need to tap again to deselect.

Derek Hewitt
  • 775
  • 8
  • 16
0

BlackBerry's answer in Swift 3. UITableView with standard cells, selecting 0 or 1 cells.

private var previousSelection = NSIndexPath()

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    let cell = tableView.cellForRow(at: indexPath)!
    if cell.accessoryType == .none {
        cell.accessoryType = .checkmark
        selection = indexPath

        if previousSelection == IndexPath() {
            previousSelection = indexPath
        } else if previousSelection != indexPath {
            let previousCell = tableView.cellForRow(at: previousSelection)
            previousCell?.accessoryType = .none
            previousSelection = indexPath
        }
    } else if cell.accessoryType == .checkmark {
        cell.accessoryType = .none
        selection = IndexPath()
    }

}
Bart van Kuik
  • 4,704
  • 1
  • 33
  • 57