UITableView Checkmark ONLY ONE Row at a Time
Solution 1
The best way is to set the accessory type in cellForRowAtIndexPath
.
Use didSelectRowAtIndexPath
to only record which path should be selected and then call reloadData
.
Solution 2
Objective-C:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}
Swift:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
Solution 3
You can create a new variable to track the index selected at didSelectRowAtIndex.
int selectedIndex;
in your cellForRowAtIndexPath
if(indexPath.row == selectedIndex)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
and in your didSelectRowAtIndex
selectedIndex = indexPath.row;
[tableView reloadData];
Solution 4
You don't need to (and shouldn't) just reload the table after each selection.
Apple has good documentation on how to manage a selection list. See Listing 6-3 for an example.
This is more or less the same answer as some of the others but I thought I'd add a little more detail.
What you want to do is save the current selected IndexPath to a variable and use that in didSelectRowAtIndexPath to remove the old check mark. This is also where you will be adding the new check mark.
You need to make sure to also set/unset the checkmarks in cellForRowAtIndexPath otherwise if your list is large and cells are reused it will look like multiple rows are selected. This is a problem with some of the other answers.
See swift 2.0 example below:
// for saving currently seletcted index path
var selectedIndexPath: NSIndexPath? = NSIndexPath(forRow: 0, inSection: 0) // you wouldn't normally initialize it here, this is just so this code snip works
// likely you would set this during cellForRowAtIndexPath when you dequeue the cell that matches a saved user selection or the default
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// this gets rid of the grey row selection. You can add the delegate didDeselectRowAtIndexPath if you want something to happen on deselection
tableView.deselectRowAtIndexPath(indexPath, animated: true) // animated to true makes the grey fade out, set to false for it to immediately go away
// if they are selecting the same row again, there is nothing to do, just keep it checked
if indexPath == selectedIndexPath {
return
}
// toggle old one off and the new one on
let newCell = tableView.cellForRowAtIndexPath(indexPath)
if newCell?.accessoryType == UITableViewCellAccessoryType.None {
newCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
let oldCell = tableView.cellForRowAtIndexPath(selectedIndexPath!)
if oldCell?.accessoryType == UITableViewCellAccessoryType.Checkmark {
oldCell?.accessoryType = UITableViewCellAccessoryType.None
}
selectedIndexPath = indexPath // save the selected index path
// do whatever else you need to do upon a new selection
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// if this is the currently selected indexPath, set the checkmark, otherwise remove it
if indexPath == selectedIndexPath {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
}
}
Solution 5
You don't need to reload the tableView.
See the below code:
Declare a NSIndexPath lastSelectedIndexPath
variable for your class
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(lastSelectedIndexPath) {
UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:lastSelectedIndexPath];
[lastCell setAccessoryView:nil];
}
UITableViewCell *currentCell = [tableView cellForRowAtIndexPath:currentIndexPath];
[currentCell setAccessoryView:UITableViewCellAccessoryCheckmark];
lastSelectedIndexPath = indexPath;
}
Comments
-
Jupiter869 over 2 years
With this code, I can check multiple rows in a table.
But what I want is to only have one row checked at a time. Here's my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; UITableViewCell *theCell = [tableView cellForRowAtIndexPath:indexPath]; if (theCell.accessoryType == UITableViewCellAccessoryNone) { theCell.accessoryType = UITableViewCellAccessoryCheckmark; } else if (theCell.accessoryType == UITableViewCellAccessoryCheckmark) { theCell.accessoryType = UITableViewCellAccessoryNone; } }
If a user selects a different row, then I want the old row to simply automatically uncheck. Don't know how to do that.
-
Nick Hingston about 12 yearsThe only problem with this solution is you will lose the deselect row animation. Just reload the other visible cells.
-
Phillip Mills about 12 yearsI definitely choose correct first and pretty later...if necessary. :-)
-
Jupiter869 about 12 yearsThank You again! reloadData was the key to making things work. Your insight much appreciated.
-
Jade almost 11 yearsCorrect and pretty is achievable in this case. So there is no need to resort to
reloadData
. -
Admin about 10 yearsDoes anyone know how to save the state of the selected row using NSUserDeafults? I've been looking for a simple way to do this. All the solution I've found are rather elaborate, and for my project that would simply clutter up my code.
-
Burhanuddin Sunelwala about 10 years@Borges could you explain your question a bit more? what exactly you mean by state of a cell?
-
Thomás Pereira over 9 yearsThe BEST solution! Thank you!
-
p0lAris almost 9 yearsThis doesn't take care of reusability. Careful.
-
umakanta over 8 yearsIt is crashing when 'checkedIndexPath' is not in the VisibleCells in the tableView if (self.checkedIndexPath != nil) { var cell = tableView.cellForRowAtIndexPath(self.checkedIndexPath!) cell?.accessoryType = .None } Cell is nil if it is not in the Visible Cells
-
Cullen SUN over 8 yearsagree with @p0lAris. When the cell get reused. it still having the check mark
-
jeffjv over 8 yearsThis is fine if your table has less rows then what would fill the screen but once they spill past and start getting reused during a scroll (as @p0lAris said), your users will be very confused.
-
Deepak Thakur about 8 yearshow is lastSelectedIndexPath initialised and managed? as lastSelectedIndexPath can never be nil
-
Deepak Thakur about 8 yearsvar lastSelectedIndexPath: NSIndexPath? = NSIndexPath(forRow: 0, inSection: 0)
-
Dasoga about 8 years
var lastSelectedIndexPath: NSIndexPath!
Is nil at start, but after, I check if is nil with the if, and at end of the function I do:lastSelectedIndexPath = indexPath
-
thephatp almost 8 years@HardikAmal - simple, just add the condition when configuring the cell to definitively set the accessory type. So, be sure to have the
else
after theif
and set the value either way. -
Arun almost 8 yearsplease declare as NSINTEGER instead of int
-
jeffjv over 7 yearsYou don't (and really shouldn't) call reloadData to solve this issue. Please see some of the other answers (like mine) to correctly handle the animations and reusability.
-
matteodv over 6 yearsThis answer is amazing but it assumes that
selectedIndexPath
is initialized, doesn't it? I mean, if there is no default value when initializing the table view,didSelectRowAtIndexPath
should try to unwrapselectedIndexPath
and set the value -
jeffjv over 6 years@matteodv, in my code snip I have a comment on the third line that says
// likely you would set this during cellForRowAtIndexPath when you dequeue the cell that matches a saved user selection or the default
. Does that answer your question? -
matteodv over 6 yearsYes, sorry... I meant a situation in which there isn't a default value and it need the be set the first time a row is clicked. In this case, cellForRowAtIndexPath won't set the variable...
-
Naresh over 5 yearsSimply this code is enough, thank you. John Paul Manoza
-
iHarshil over 5 yearsSimply Superb Man! This solution also works for keep selecting anything to a cell. Great! You saved my day! :)
-
As If Prince about 3 yearsyou saved my day
-
pkamb over 2 yearsIsn't calling
tableView.cellForRowAtIndexPath( ... )
essentially the same as callingtableView.reloadRows(at: ...)
? If you reload the old and new rows (not the entire table view) you can remove the duplicated checkmark code fromdidSelect
.