UI table view cell check marks repeating swift - uitableview

My when a table cell is checked and you scroll down a check mark is repeated.
I know this is due to cell reuse, but don't know how to fix it.
function to populate table
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item = self.myEvents[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("row", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = self.myEvents[indexPath.row]
return cell
}
//function to make the table checkable
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("indexpath: \(indexPath)")
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
let text = cell.textLabel!.text
if cell.accessoryType == UITableViewCellAccessoryType.None {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
//let text = cell.textLabel!.text
if(checkedEvents[0] == ""){
checkedEvents[0] = text!
}else{
checkedEvents.append(text!)
}
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
var index = 0
for event in checkedEvents{
if event == text{
self.checkedEvents.removeAtIndex(index)
index++
}
}
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}

First, you need to store the number of the selected row somewhere. How about self.selectedRowNumber?
var selectedRowNumber: Int? = nil
Set this when the user selects a row (short version):
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
cell.accessoryType = .Checkmark
self.selectedRowNumber = indexPath.row
// You'll also need some code here to loop through all the other visible cells and remove the checkmark from any cells that aren't this one.
}
Now modify your -tableView:cellForRowAtIndexPath: method to clear the accessory if it's not the selected row, or add it if it is:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("row", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = self.myEvents[indexPath.row]
cell.accessoryType = .None
if let selectedRowNumber = self.selectedRowNumber {
if indexPath.row == selectedRowNumber {
cell.accessoryType = .Checkmark
}
}
return cell
}
This code was written here in the browser, and may need some fixes to compile.

If you want only one selection, put tableView.reloadData() in your didSelectRowAtIndexPath function

Related

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
}

Checkmarks in UITableViewCells showing incorrectly

I have a small scrollable tableView which displays roughly eight rows, when I select a row a checkmark appears, when I select it again it disappears.
The issue is, as I scroll down and the cells are reused, more rows are automatically checked. What is the best practice for tracking which rows require a checkmark so that this does not happen. I have looked everywhere but haven't found Swift solution that works well.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = "\(searchResults.usernameUserSearchArray[indexPath.row])"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)
if selectedCell?.accessoryType == UITableViewCellAccessoryType.Checkmark {
selectedCell?.accessoryType = UITableViewCellAccessoryType.None
} else {
selectedCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
tableView.reloadData()
}
Wouldn't something like this do it for you? I haven't tested it since I am not on my Mac.
var selectedCells = [NSIndexPath]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")!
cell.accessoryType = selectedCells.contains(indexPath) ? .Checkmark : .None
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)
selectedCells.append(indexPath)
if selectedCell?.accessoryType == .Checkmark {
selectedCell?.accessoryType = .None
selectedCells = selectedCells.filter {$0 != indexPath}
} else {
selectedCell?.accessoryType = .Checkmark
}
}
In didSelectRowAtIndexPath create an NSMutableArray which stores selected index of the cell and in cellForRowAtIndexPath check using if condition from array whether index path is available in array or not. If available than set accessory type checkmark true for index path.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = arr.objectAtIndex(indexPath.row) as? String
if(arr .objectAtIndex(indexPath.row) as! String == selectedValue.objectAtIndex(indexPath.row) as! String)
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
}
else
{
cell.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)
if selectedCell?.accessoryType == UITableViewCellAccessoryType.Checkmark {
selectedCell?.accessoryType = UITableViewCellAccessoryType.None
selectedValue .removeObject(arr .objectAtIndex(indexPath.row))
} else {
selectedCell?.accessoryType = UITableViewCellAccessoryType.Checkmark
selectedValue .addObject(arr .objectAtIndex(indexPath.row))
}
}

TableViewCell scroll selects wrong cells when scrolling

My tableview highlights the wrong cells when I scroll up and down. In didSelectRowAtIndexPath, I'm trying to add the .Checkmark accessory view as well as change the font of cell.textLabel?.text. Upon scrolling, random rows are selected and the ones I manually select sometimes deselect.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! MyCell
cell.textLabel?.text = self.array[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
if cell.accessoryType == UITableViewCellAccessoryType.Checkmark {
cell.accessoryType = .None
cell.textLabel!.font = UIFont(name: "HelveticaNeue", size: 16)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
} else if cell.accessoryType == UITableViewCellAccessoryType.None {
cell.accessoryType = .Checkmark
cell.textLabel!.font = UIFont(name: "HelveticaNeue-Bold", size: 16)
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
Declare array which will keep track of which rows are selected:
var selectedRows:[Bool] = []
You need to make sure that it is populated with the same number of items as your data array.
Then modify your cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! MyCell
cell.textLabel?.text = self.array[indexPath.row]
if selectedRows[indexPath.row] {
// row selected
cell.accessoryType = .Checkmark
}
else {
cell.accessoryType = .None
}
return cell
}
And in your didSelectRow function add line:
selectedRows[indexPath.row] = !selectedRows[indexPath.row]
When you modify your cell from didSelectRow the next time cell is dequeued it will have the accessory type which you set. The reason why it happens after scroll is because when you scroll the cells which become visible get dequeued. So, you need to manually set correct accessory type for them

table cells show first index's data of NSMutableArray in swift

I have a NSMutableArray (array2) as data source of table view. I want to add object to that array when i select a cell of searchResultsTableView and reload the self.tableView with that array.
If i add object with array2.addObject() method, then all the cells are okay with individual data.
But, if i add object with array2.insertObject(myObject, atIndex: 0), then all the cells show same data as data of array2[0]. Why ?
My problem is in didSelectRowAtIndexPath function of table view. I always want to add the selected object at the first position of my table view, thats why i implemented with insertObject method instead of addObject method. Below is my code portion.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.searchDisplayController!.searchResultsTableView {
return self.array1.count
}else{
return self.array2.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if tableView == self.searchDisplayController!.searchResultsTableView {
let number = self.array1[indexPath.row]
cell.textLabel?.text = String(number)
} else {
let cell: customCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as customCell
let brand = self.array2[indexPath.row] as NSString
cell.name.text = brand
cell.comment.text = "100"
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView == self.searchDisplayController!.searchResultsTableView {
let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
self.array2.insertObject(cell.textLabel!.text!, atIndex: 0)
//self.array2.addObject(cell.textLabel!.text!)
self.searchDisplayController!.setActive(false, animated: true)
self.tableView.reloadData()
}
}
your cellForRowAtIndexPath method is weird!... your always returning "let cell = UITableViewCell()" and not in fact your dequeued "cell"
change your method to this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.searchDisplayController!.searchResultsTableView {
let number = self.array1[indexPath.row]
let cell = UITableViewCell()
cell.textLabel?.text = String(number)
return cell
} else {
let cell: customCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as customCell
let brand = self.array2[indexPath.row] as NSString
cell.name.text = brand
cell.comment.text = "100"
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