UITableviewCell toggling only one custom checkmark state not working - Swift 3 - ios

I have reviewed this link and the solutions were not working for me.
I am trying to select one row and when I do select it, it adds a checkmark to the label. If another row is selected while there is an existing checkmark, it will uncheck the previous one which is stored in a selectedIndexPath variable.
It works in the beginning, however when scrolling through the tableview several times, I occasionally see a cell selected that should not be as indicated in this image:
What I am doing when a user selects a cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? CustomCell {
let customCell = customCellData[indexPath.row]
customCell.toggleSelected()
cell.configureCheckmark(with: customCell)
}
if let oldIndexPath = selectedIndexPath, let cell = tableView.cellForRow(at: oldIndexPath) as? CustomCell, oldIndexPath.row != indexPath.row {
let customCell = customCellData[oldIndexPath.row]
customCell.toggleSelected()
cell.configureCheckmark(with: customCell)
}
if let selected = selectedIndexPath, selected.row == indexPath.row {
selectedIndexPath = nil
tableView.deselectRow(at: indexPath, animated: true)
} else {
selectedIndexPath = indexPath
}
}
and in for cellForRowAt: (is it redundant to check the selectedIndexPath and the state in the model?)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
let customCell = customCellData[indexPath.row]
cell.customCell = customCell
if selectedIndexPath == indexPath {
cell.checkLabel.text = "✔️"
} else {
cell.checkLabel.text = ""
}
return cell
}
and finally in the CustomCell:
var customCell: CustomCell? {
didSet {
if let customCell = customCell {
configureCheckmark(with: customCell)
}
}
}
func configureCheckmark(with customCell: CustomCellData) {
if customCell.isSelected {
checkLabel.text = "✔️"
} else {
checkLabel.text = ""
}
}
In CustomCellData I toggle the state as follows:
class CustomCellData {
var isSelected = false
func toggleSelected() {
isSelected = !isSelected
}
}
I am scratching my head on this and unsure what to do, any help would be great.

The easiest solution is to reduce didSelectRowAt to
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
selectedIndexPath = indexPath
tableView.reloadData()
}
This updates all visible cells properly.
Or more sophisticated version which updates only the affected rows and checks if the cell is already selected
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if selectedIndexPath != indexPath {
let indexPathsToReload = selectedIndexPath == nil ? [indexPath] : [selectedIndexPath!, indexPath]
selectedIndexPath = indexPath
tableView.reloadRows(at: indexPathsToReload, with: .none)
} else {
selectedIndexPath = nil
tableView.reloadRows(at: [indexPath], with: .none)
}
}
The code in cellForRowAt does the rest.

Related

Multiple Cell Selection on TableView

I'm having an issue where when I'm selecting the cell for e.g at index 3 , it selecting cells below also at random indexes. Check and Uncheck cell is working but for some reasons when selecting a cell it is selecting other cells as well. My array is returning 120 rows in total. I have selected multiple touch. Thank you for the help.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return arrayVerbCount.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MesTalentsChoixCell
cell.verb!.text = arrayVerbCount[indexPath.row].verb
if cell.isSelected
{
cell.isSelected = false
if cell.accessoryType == UITableViewCellAccessoryType.none
{
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.none
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let cell = tableView.cellForRow(at: indexPath)
if cell!.isSelected
{
cell!.isSelected = false
if cell!.accessoryType == UITableViewCellAccessoryType.none
{
cell!.accessoryType = UITableViewCellAccessoryType.checkmark
}
else
{
cell!.accessoryType = UITableViewCellAccessoryType.none
}
}
}
My custom cell class:
class MesTalentsChoixCell: UITableViewCell {
#IBOutlet weak var verb: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
You should do like this way, this is very much easy solution if there is only one section.
Initialize selectedItems array like this,
var selectedItems: [Int] = []
Find UITableViewDataSource method below
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.tmpValue.text = data[indexPath.row]
if selectedItems.contains(indexPath.row) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
Find UITableViewDelegate Method below.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if selectedItems.contains(indexPath.row) {
let index = selectedItems.index(of: indexPath.row)
selectedItems.remove(at: index!)
} else {
selectedItems.append(indexPath.row)
}
tableView.reloadRows(at: [indexPath], with: .none)
}
Code will be changed depending on your requirement and custom cell. Hope you can do it your way. Thank you.
UPDATE
You can even use Set also like this way,
var setSelectedItems: Set<Int> = []
UITableViewDataSource method,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.tmpValue.text = data[indexPath.row]
if setSelectedItems.contains(indexPath.row) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
UITableViewDelegate method,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if setSelectedItems.contains(indexPath.row) {
setSelectedItems.remove(indexPath.row)
} else {
setSelectedItems.insert(indexPath.row)
}
tableView.reloadRows(at: [indexPath], with: .none)
}
Make bool array for stability while scrolling i.e.
var arrStatusBool = [Bool]()
Now set value at indexPath.row in didSelectRowAt
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if self.arrStatusBool[indexPath.row]
{
self.arrStatusBool[indexPath.row] = false
}
else
{
self.arrStatusBool[indexPath.row] = true
}
}
And also put this in cellForRowAt to avoid scrolling issue.
if self.arrStatusBool[indexPath.row]
{
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
else
{
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
hope this help!
For multiple selection you should track selected cells in a Dictionary for convenience faster access to selected and unselected indexPaths allowing you use multiple sections because the key value of our Dictionary is a string formed by (IndexPath.section)+(IndexPath.row) which is always unique combination
var selectedIndexPaths : [String:Bool] = [:]
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
self.selectedIndexPaths[currentIndexPathStr] = true
}else{
self.selectedIndexPaths[currentIndexPathStr] = false
}
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.accessoryType = .checkmark
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
cell.accessoryType = .none
}
return cell
}
Results
Just a minor change
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MesTalentsChoixCell
cell.verb!.text = arrayVerbCount[indexPath.row].verb
if cell.isSelected
{
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.none
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let cell = tableView.cellForRow(at: indexPath)
//toggle the state
cell!.isSelected = !cell!.isSelected
if cell!.isSelected
{
cell!.accessoryType = UITableViewCellAccessoryType.checkmark
}
else
{
cell!.accessoryType = UITableViewCellAccessoryType.none
}
}
Note: you should also create method for common code :-)

UITableViewCell has incorrect change on indexPath

I try to use UITableViewCell's accessoryType property to checkmark cell when clicked ,but when cell selected , the checkmark set several time for different cells for example when I select row [0] , row [0] and row [8] and row [17] AccessoryType set to checkmark !
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .checkmark
tableView.reloadData()
}
For single selection, you need to track your selected indexPath in a viewController variable,
var selectedIndexPath : IndexPath?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndexPath = indexPath
tableView.reloadData()
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.accessoryType = .none
cell.selectionStyle = .none
if(indexPath == selectedIndexPath) {
cell.accessoryType = .checkmark
}
return cell
}
Even better (avoiding reload the entire UITableView)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let previousSelectedIndexPath = self.selectedIndexPath
self.selectedIndexPath = indexPath
if(previousSelectedIndexPath != nil) {
self.tableView.reloadRows(at: [previousSelectedIndexPath!,self.selectedIndexPath!], with: .automatic)
}else{
self.tableView.reloadRows(at: [self.selectedIndexPath!], with: .automatic)
}
self.tableView.reloadData()
}
UPDATE, Allowing multiple selection
For multiple selection you should track selected cells in a Dictionary for convenience faster access to selected and unselected indexPaths allowing you use multiple sections because the key value of our Dictionary is a string formed by (IndexPath.section)+(IndexPath.row) which is always unique combination
var selectedIndexPaths : [String:Bool] = [:]
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
self.selectedIndexPaths[currentIndexPathStr] = true
}else{
self.selectedIndexPaths[currentIndexPathStr] = false
}
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.accessoryType = .checkmark
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
cell.accessoryType = .none
}
return cell
}
Results

UItableview on one click multiple cell are getting checked swift 3

I am having a problem in uitableview whenever I select one cell 4th no cell auto get selected... I am tired of this problem someone please help here is my didselectrowfunc..
////////////////For Adding Check Box////////////////////////
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCellAccessoryType.checkmark
{
if selectedDisease.count > 0
{
let no = selectedDisease.index(of: finall[indexPath.row])
selectedDisease.remove(at: no!)
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none
print(selectedDisease)
}
else
{
selectedDisease = [""]
}
}
else
{
if selectedDisease.contains("")
{
selectedDisease.removeAll()
var name = finall[indexPath.row]
selectedDisease.append(name)
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
print(selectedDisease)
}
else
{
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
selectedDisease.append(finall[indexPath.row])
//selectedDisease = [finall[indexPath.row]]
print(selectedDisease)
}
}
}
/////////CellForRowAt///////////////
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "diseasetable", for: indexPath) as! FoodTableViewCell
cell.DiseaseName.text = finall[indexPath.row]
//indexsave.append(indexPath.row)
return cell
}
Try this logic hope this will solve your problem
let dict = [NSIndexPath: String]()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
if let _ = // dict contains value at indexpath {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if ( // dict contains value == indexpath) {
// remove value from dict
} else {
//add value to dict
}
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
Just take the advantage of prepareForReuse method of UITableViewCell. In your FoodTableViewCell class implement the prepareForReuse menthod like following
class FoodTableViewCell: UITableViewCell
{
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func prepareForReuse()
{
self.accessoryType = UITableViewCellAccessoryType.none
//add code to reset selection if there is any other selection mechanism
}
}

indexPathForSelectedRow returns nil although I have a cell programmatically selected

I have a UITableView which I load with some application settings. I need the table to be single-select, and since the table holds settings there might be a chance some cell will be programmatically selected based on the setting enabled status.
Currently, I'm experiencing a weird behaviour where if I programmatically select a cell then indexPathForSelectedRow returns nil (when it should return the index for the cell I selected), thus when I tap on a second cell (to change the currenty selected setting) I end up with two cells selected (even when I set allowMultipleSelection to false in my tableView object).
Here's my code:
override func viewDidLoad() {
tableView.allowsMultipleSelection = false
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("myCell")
if let cell = cell {
// tableObject is an array containing settings model
let row = tableObject[indexPath.row]
cell.textLabel!.text = row.settingValue
if row.selected {
cell.setSelected(true, animated: false)
cell.accessoryType = .Checkmark
}
cell.tag = row.id
}
return cell!
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
// oldIndex is always nil for the cell I called setSelected in cellForRowAtIndexPath
if let oldIndex = tableView.indexPathForSelectedRow {
if let oldCell = tableView.cellForRowAtIndexPath(oldIndex) {
tableView.deselectRowAtIndexPath(oldIndex, animated: true)
oldCell.accessoryType = .None
}
}
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.setSelected(true, animated: true)
cell.accessoryType = .Checkmark
}
return indexPath
}
override func tableView(tableView: UITableView, willDeselectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.accessoryType = .None
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
return indexPath
}
Any idea how I can always have only one cell selected at a time?
Thanks!
Just in case someone else comes across this same behaviour, it seems that selecting the cell through cell.setSelected() it's not the same as invoking tableView.selectRowAtIndexPath() method. Selecting the row with the latest does the job perfectly and solves the issue.
Note that calling tableView.reloadData() resets the tableView.indexPathForSelectedRow to nil.
here how you can accomplish table view single selection through tableView.indexPathForSelectedRow :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.accessoryType = tableView.indexPathForSelectedRow == indexPath ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if let visiblePaths = tableView.indexPathsForVisibleRows
{
for visibleIndexPath in visiblePaths
{
let cell = tableView.cellForRow(at: visibleIndexPath)
if visibleIndexPath == indexPath
{
cell?.accessoryType = .checkmark
}
else
{
cell?.accessoryType = .none
}
}
}
}
Create an array like
var arrSelectCell = [NSIndexPath]()
And do the code at didSelectRowAtIndexPath like following:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if arrSelectCell.contains(indexPath) {
if let oldCell = tableView.cellForRowAtIndexPath(indexPath) {
if let index = arrSelectCell.indexOf(indexPath) {
arrSelectCell.removeAtIndex(index)
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
oldCell.accessoryType = .None
}
}
else
{
arrSelectCell.append(indexPath)
if let selectCell = tableView.cellForRowAtIndexPath(indexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
selectCell.accessoryType = .Checkmark
}
}
and also dont forget to set a code in cellForRowAtIndexPath to check already checked and unchecked cell maintain after reuse cell also. So need to few code you need to write as following.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("serchCell", forIndexPath: indexPath) as! SearchTableViewCell
if arrSelectCell.contains(indexPath)
{
cell.accessoryType = .Checkmark
}
else
{
cell.accessoryType = .None
}
return cell
}

UITableView Checkmark ONLY ONE Row at a Time

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.
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
}
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];
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.
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
}
}
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;
}
For Swift 3 following worked for me :
override func viewDidLoad() {
super.viewDidLoad()
// allow cells to be selected
tableView.allowsSelection = true
// ensure that deselect is called on all other cells when a cell is selected
tableView.allowsMultipleSelection = false
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableVIew.cellForRow(at: indexPath as IndexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableVIew.cellForRow(at: indexPath as IndexPath)?.accessoryType = .none
}
I think https://stackoverflow.com/a/5960016/928599 will help you.
I used it and it Works with deselectRowAtIndexPath too!
Simplest way is to save the selected IndexPath and check it in cellForRowAtIndexPath:
Initialize the selected index path:
selectedIndexPath = [[NSIndexPath alloc] init];
In cellForRowAtIndexPath:
if([selectedIndexPath isEqual:indexPath]){
[checkMark setHidden:NO];
} else {
[checkMark setHidden:YES];
}
In didSelectRowAtIndexPath:
selectedIndexPath = indexPath;
[tableView reloadData];
It should work work try this Njoy :)
Try this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 1 {
// un-select the older row
if let selected = self.LastSelected {
tableView.cellForRowAtIndexPath(selected)?.accessoryType = .None
}
// select the new row
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
self.LastSelected = indexPath
}
}
In Swift, following works perfectly:
lastSelectedIndexPath = NSIndexPath(forRow: 1, inSection: 0)//Row will be the previous selected row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:LabelsSelectionCell = tableView.dequeueReusableCellWithIdentifier("LabelsSelectionCell", forIndexPath: indexPath) as! LabelsSelectionCell
cell.setCellLael(labelOptionsArray[indexPath.row])
if lastSelectedIndexPath == indexPath
{
cell.setCellCheckedStatus(true)
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if let _ = lastSelectedIndexPath
{
let lastCell:LabelsSelectionCell = tableView.cellForRowAtIndexPath(lastSelectedIndexPath!) as! LabelsSelectionCell
lastCell.setCellCheckedStatus(false)
}
let currentCell:LabelsSelectionCell = tableView.cellForRowAtIndexPath(indexPath) as! LabelsSelectionCell
currentCell.setCellCheckedStatus(true)
lastSelectedIndexPath = indexPath
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
In Swift 2.0 I used this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if((lastSelectedIndexPath) != nil) {
let lastCell = tableView.cellForRowAtIndexPath(lastSelectedIndexPath)
lastCell?.accessoryType = UITableViewCellAccessoryType.None
}
let currentCell = tableView.cellForRowAtIndexPath(indexPath)
currentCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
lastSelectedIndexPath = indexPath
}
My way is deselect other cells during selecting:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ....
if condition {
cell.accessoryType = .checkmark
tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.bottom)
} else {
cell.accessoryType = .none
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
for row in 0...tableView.numberOfRows(inSection: 0) {
if row == indexPath.row {continue}
tableView.deselectRow(at: IndexPath(row: row, section: 0), animated: true)
}
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .none
}
Here's what I came up when I had this problem.
This code is implemented in the latest version of Swift, as of today...
For more info and/or the extension file, please check out the Github Gist of this snippet.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let sip = selectedIndex {
tableView.deselectRow(at: sip, animated: false)
}
selectedIndex = indexPath
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if selectedIndex?.row == indexPath.row {
selectedIndex = nil
}
}
tested and solved try this its worked perfectly
add this as global variable
var selectedIndexPath = NSIndexPath(row: 0, section: 0)
Add this in didselect method
// old one check off
if indexPath != selectedIndexPath as IndexPath {
let oldCell = categorytable.cellForRow(at: selectedIndexPath as IndexPath)
if oldCell?.accessoryView != nil {
oldCell?.accessoryView = nil
}
else {
let imageName = "checkmark"
let image: UIImageView = UIImageView(image: UIImage(named: imageName))
cell.accessoryView = image
}
}
// the new one on
if cell.accessoryView == nil {
let imageName = "checkmark"
let image: UIImageView = UIImageView(image: UIImage(named: imageName))
cell.accessoryView = image
}
else {
cell.accessoryView = nil
}
You can do it in custom cell type in one line of code.
final class CheckableTableViewCell: UITableViewCell {
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.accessoryType = selected ? .checkmark : .none
}
}
To make this method work - your cell should be selected.
If you don’t want to see the highlight of the selected cell, just set cell's selectionStyle to .none in storyboard or in code
This method will not work, if you will call
tableView.deselectRow(at: indexPath, animated: true)
Also works great with multiple selection.
Swift 5 version of jeffjv's response.
// for saving currently selected 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: IndexPath) {
// this gets rid of the grey row selection. You can add the delegate didDeselectRowAtIndexPath if you want something to happen on deselection
tableView.deselectRow(at: 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! as IndexPath {
return
}
// toggle old one off and the new one on
let newCell = tableView.cellForRow(at: indexPath)
if newCell?.accessoryType == UITableViewCell.AccessoryType.None {
newCell?.accessoryType = UITableViewCell.AccessoryType.Checkmark
}
let oldCell = tableView.cellForRow(at: selectedIndexPath! as IndexPath)
if oldCell?.accessoryType == UITableViewCell.AccessoryType.Checkmark {
oldCell?.accessoryType = UITableViewCell.AccessoryType.None
}
selectedIndexPath = indexPath as NSIndexPath // save the selected index path
// do whatever else you need to do upon a new selection
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// if this is the currently selected indexPath, set the checkmark, otherwise remove it
if indexPath == selectedIndexPath! as IndexPath {
cell.accessoryType = UITableView.CellAccessoryType.Checkmark
} else {
cell.accessoryType = UITableView.CellAccessoryType.None
}
}
Swift iOS
var checkedIndexPath : NSIndexPath?
// table row which row was selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
println("Section #\(indexPath.section)! You selected cell #\(indexPath.row)!")
if (self.checkedIndexPath != nil) {
var cell = tableView.cellForRowAtIndexPath(self.checkedIndexPath!)
cell?.accessoryType = .None
}
var cell = tableView.cellForRowAtIndexPath(indexPath)
cell?.accessoryType = .Checkmark
self.checkedIndexPath = indexPath
}// end tableView
Store the selected index path, update it in didSelectRowAt:, and from there call reloadRows(at: to reload only the old and new checked rows (not the entire table view):
var selectedIndexPath: IndexPath?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let previous = selectedIndexPath
selectedIndexPath = indexPath
let allowDeselection = true // optional behavior
if allowDeselection && previous == selectedIndexPath {
selectedIndexPath = nil
}
tableView.reloadRows(at: [previous, selectedIndexPath].compactMap({ $0 }), with: .automatic)
tableView.deselectRow(at: indexPath, animated: true)
}
Handle the setting of the checkmark accessory view in cellForRowAt indexPath:. This will account for cell reuse and also be triggered by your above calls to reloadRows(at:, avoiding duplicating the code of setting the cell accessory:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IDENTIFIER", for: indexPath)
cell.accessoryType = (indexPath == selectedIndexPath) ? .checkmark : .none
// ...
return cell
}

Resources