UItableview on one click multiple cell are getting checked swift 3 - ios

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
}
}

Related

TextLabel colour automatically changed when i Scroll UiTableView

I changing textlabel color on didSelectRowAt but when I scroll UITableView it also effects in other textlabel also
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
if (cell.LBLIntrest.textColor == (UIColor.black))
{
cell.LBLIntrest.textColor = Uicolor.blue
} else {
cell.LBLIntrest.textColor = Uicolor.black
}
}
First you have to create property to hold selected cell like below
/* To hold selected cell */
var selectedIndexPath :IndexPath?
After that set color of selected cell in cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") {
cell.textLabel?.text = "Row Number: \(indexPath.row)"
/* Check if cell is selected then set layout accourding to your requirements */
if indexPath == selectedIndexPath {
cell.textLabel?.textColor = .blue
} else {
cell.textLabel?.textColor = .black
}
return cell
}
return UITableViewCell()
}
After this manage when user select a cell in didSelectRowAt
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Toggle if user seleted same cell
if selectedIndexPath == indexPath {
if let cell = tableView.cellForRow(at: indexPath) {
/* Check and toggle selected cell color */
cell.textLabel?.textColor = cell.textLabel?.textColor == .black ? .blue : .black
}
} else {
/* set color of seleted cell */
if let cell = tableView.cellForRow(at: indexPath) {
cell.textLabel?.textColor = .blue
}
}
/* Save which cell is selected */
selectedIndexPath = indexPath
}
And last manage didDeselectRowAt
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
/* Remove if deselect same cell */
if selectedIndexPath == indexPath {
selectedIndexPath = nil
}
/* Change color to black */
if let cell = tableView.cellForRow(at: indexPath) {
cell.textLabel?.textColor = .black
}
}
This code is for on cell selection at one time so you have to set
tableView.allowsMultipleSelection = false
Hope this helps.
This should work for you.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
setSelectedColor(cell: cell)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
...
if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) {
setSelectedColor(cell: cell)
}
return cell
}
func setSelectedColor(cell: UITableViewCell) {
if (cell.LBLIntrest.textColor == (UIColor.black)) {
cell.LBLIntrest.textColor = Uicolor.blue
} else {
cell.LBLIntrest.textColor = Uicolor.black
}
}
But, I would recommend moving that cell.LBLIntrest.textColor = Uicolor.blue to UITableViewCell under func setSelected(_ selected: Bool, animated: Bool) method
class TableViewCell: UITableViewCell {
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// label textColor logic goes here
// make use of selected
}
}

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 :-)

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
}

UITableViewCell Checkmark Being Added to multiple rows when one is tapped

I have a tableview where not all of the cells are visible at once. I am trying to make it so that when a row is tapped it adds a checkmark accessory to the cell. My issue is that it adds it to other rows as well. In my table view there are 4 rows fully showing and a fifth one barely showing. If I check the first box it will then add a check mark to every fifth box (e.g. indexPath.row = 0,5,10,15...) despite the indexPath.row being different.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: DropDownMenuCell = tableView.dequeueReusableCellWithIdentifier("DropDownMenuCell", forIndexPath: indexPath) as! DropDownMenuCell
cell.dropDownCellLabel?.text = DropDownItems[indexPath.row].Name
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell: DropDownMenuCell = tableView.cellForRowAtIndexPath(indexPath) as! DropDownMenuCell
print(indexPath.row)
if selectedCell.accessoryType == .None {
selectedCell.accessoryType = .Checkmark
} else {
selectedCell.accessoryType = .None
}
}
Edit: Apologies for the duplicate, my initial search for this question didn't show the other question. I got a working answer here in swift already or I would try and go through the objective c post to solve my problem.
Maintain in your Data source which cell is to be selected.
Then in cellForRowAtIndexPath:
if (DropDownItems[indexPath.row].isSelected) {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
and in your didSelectRowAtIndexPath Method:
if(DropDownItems[indexPath.row].isSelected) {
DropDownItems[indexPath.row].isSelected = false
} else {
DropDownItems[indexPath.row].isSelected = true
}
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
In Swift 3, this should help:
import UIKit
class ViewController: UITableViewController {
let foods = ["apple", "orange", "banana", "spinach", "grape"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return foods.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = foods[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCellAccessoryType.checkmark
{
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none
}
else
{
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

UITableViewCell checkmark to be toggled on and off when tapped

I'm working on a tableview
I want to be able to tap on each cell and when tapped, it displays a checkmark on the cell
Now I have some code that makes this work:
// checkmarks when tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let section = indexPath.section
let numberOfRows = tableView.numberOfRowsInSection(section)
for row in 0..<numberOfRows {
if let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section)) {
cell.accessoryType = row == indexPath.row ? .Checkmark : .None
}
}
}
but this code only selects 1 cell inside a section (I have 5 sections)
I need it to select any cell anywhere
Also when I drag my screen up and down I lose by checkmark
viewcontroller.swift
class ViewController: UIViewController, UITableViewDataSource { //class and subclass |)
//---------------------------------------------------------------------------------------------------------------------------/
// Variable and constant, also IBAOutlet
let section1 =
["this is used",
"this is used to test",
"this is used to test the lenght",
"this is used to test the lenght of the text",
"this is used to test the lenght of the text",
"this is used to test the lenght of the text",
"this is used to test the lenght of the text",
"this is used to test the lenght of the text",
"this is used to test the lenght of the text",]
let section2 =
["this is used to test the lenght of the text"]
let section3 =
["this is",
"this is ",]
#IBOutlet weak var scoreshow: UILabel!
#IBOutlet weak var reset: UIButton!
#IBOutlet weak var tableView: UITableView!
// --------------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//----------------------------------------------------------------------------------------
// checkmarks when tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark
{
cell.accessoryType = .None
}
else
{
cell.accessoryType = .Checkmark
}
}
}
//----------------------------------------------------------------------------------------
//number of sections for the table
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 5
}
//----------------------------------------------------------------------------------------
//Calculate the amount of rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.section1.count;
}
//----------------------------------------------------------------------------------------
//Cells text label and config
func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier:"cell")
cell.textLabel!.text = section1[indexPath.row]
cell.textLabel!.numberOfLines = 0
return cell
}
//----------------------------------------------------------------------------------------
#IBAction func resetswitch(sender: UIButton) {
}
//----------------------------------------------------------------------------------------
}
Swift > 3.0
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
I solved by using two Swift functions: the didSelectRowAtIndexPath and the didDeselectRowAtIndexPath.
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.accessoryType = .None
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
cell.accessoryType = .Checkmark
}
}
To make this work properly, add a line of code to your cellForRowAtIndexPath function to select a row when the table view is drawn on the screen, otherwise the didDeselectRowAtIndexPath will not be called the first time you select another row. Like so:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellData", forIndexPath: indexPath)
if (some condition to initially checkmark a row)
cell.accessoryType = .Checkmark
tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.Bottom)
} else {
cell.accessoryType = .None
}
return cell
}
Try this:
var checked = [Bool]() // Have an array equal to the number of cells in your table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
//configure you cell here.
if !checked[indexPath.row] {
cell.accessoryType = .None
} else if checked[indexPath.row] {
cell.accessoryType = .Checkmark
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
checked[indexPath.row] = false
} else {
cell.accessoryType = .Checkmark
checked[indexPath.row] = true
}
}
}
To reset all the checkboxes:
func resetChecks() {
for i in 0.. < tableView.numberOfSections {
for j in 0.. < tableView.numberOfRowsInSection(i) {
if let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: j, inSection: i)) {
cell.accessoryType = .None
}
}
}
}
A UITableView keeps selected state for single or multiple selections. So IMO there would need to be a very good reason for keeping an entire parallel state somewhere. If you want to just change the cell's appearance based on select state, do it in the cell.
In your UITableViewCell subclass, override setSelected like so:
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.accessoryType = selected ? .checkmark : .none
}
No need to use any table view delegate methods.
Note: You have to call super.setSelected otherwise the cell doesn't keep the selected state correctly.
Swift 3.0
Using just one function to keep it simple
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
} else {
cell.accessoryType = .checkmark
}
}
}
Swift 3.0
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
Swift 4.0, all together now:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var yourData = ["Cool","Sweet","Awesome"]
var checked = [Bool]()
override func viewDidLoad() {
super.viewDidLoad()
checked = Array(repeating: false, count: yourData.count)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchData.count
}
func tableView(_ tableView: UITableView, cellForRowAt IndexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
//configure you cell here.
if checked[IndexPath.row] == false{
cell.accessoryType = .none
} else if checked[IndexPath.row] {
cell.accessoryType = .checkmark
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
}
}
Swift 5.0
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
resetChecks()
cell.accessoryType = .checkmark
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
func resetChecks() {
for i in 0..<tableView.numberOfSections {
for j in 0..<tableView.numberOfRows(inSection: i) {
if let cell = tableView.cellForRow(at: IndexPath(row: j, section: i)) {
cell.accessoryType = .none
}
}
}
}
The simple solution as others have pointed out would be to .checkmark the row in the didSelectRowAt method and set the row to .none in the didDeselectRowAtmethod as follow...
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
}
But if you have a default row selected when the table loads you first need to deselect it when other rows are selected, in that case, use the code below instead in the didSelectRowAt method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
for row in 0..<tableView.numberOfRows(inSection: indexPath.section) {
if let cell = tableView.cellForRow(at: IndexPath(row: row, section: indexPath.section)) {
cell.accessoryType = row == indexPath.row ? .checkmark : .none
}
}
}
Updated In swift 4.2
Every New selection Remove previous Check mark
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(self.coloursArray[indexPath.row])
self.tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
self.tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
For Swift 5:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
cell.accessoryType = .checkmark
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
cell.accessoryType = .none
}
}
The simplest solution that did it for me (Swift 5.2)
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
// Remove checkmark from the row that is currently showing it before adding to one being selected
if let currentIndexPath = tableView.indexPathForSelectedRow {
self.tableView.cellForRow(at: currentIndexPath)?.accessoryType = .none
}
return indexPath
}
override public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
For anyone using a single use of a checkmark.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// checkmark logic
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType != .checkmark {
resetChecks()
cell.accessoryType = .checkmark
}
}
}
func resetChecks() {
for i in 0..<tableView.numberOfSections {
for j in 0..<tableView.numberOfRows(inSection: i) {
if let cell = tableView.cellForRow(at: NSIndexPath(row: j, section: i) as IndexPath) {
cell.accessoryType = .none
}
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if self.checkedIndex == indexPath.row{
}else{
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .checkmark
let indexPathh = IndexPath(row: checkedIndex, section: 0)
let UnCheckCell = tableView.cellForRow(at: indexPathh)
UnCheckCell?.accessoryType = .none
checkedIndex = indexPath.row
}
}
Since I didn't see anyone list this, you can create a custom UITableViewCell that will toggle the checkmark with selection by overriding it's setSelected() method and defaulting .selectionStyle to .gray:
class CheckableTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .gray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
accessoryType = selected ? .checkmark : .none
}
}
I have used tableView(_:didSelectRowAt:), delegate method to accomplish this feature of putting check mark on the cell and removing it when the cell is tapped again.
Here is the code:
//MARK:-create delegate methode that is fired when a cell is clicked
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath , animated: true)
if let cell = tableView.cellForRow(at: indexPath){
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
}
else {
cell.accessoryType = .checkmark
}
}
}

Resources