Swift UITableview cell checkmark with selection blink option - ios

My scenario, I am trying to create UITableview single cell checkmark selection and Multiple cell check mark selection. Here, I am trying by below code for single selection check mark but Its reusability not working. Also, I don't know how to do multiple cell selection by clicking cell section.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.deselectRow(at: indexPath, animated: true)
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}

For reusability, you need a variable to save the selected cell index(or array for cells) and then set the appropriate accessoryType when displaying the cell.
For single selection, remember to clear the previous selection.
A sample pattern for single selection:
var selectedRow = 0
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedRow = indexPath.row
for cell in tableView.visibleCells { //Why not using didDeselectRowAt? Because the default selected row(like row 0)'s checkmark will NOT be removed when clicking another row at very beginning.
cell.accessoryType = .none
}
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.accessoryType = indexPath.row == selectedRow ? .checkmark : .none
}
A pattern for multiple selections (Updated for multi-sections):
var selectedIndexPaths = Set<IndexPath>()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndexPaths.contains(indexPath) { //deselect
selectedIndexPaths.remove(indexPath)
tableView.cellForRow(at: indexPath)?.accessoryType = .none
}
else{
selectedIndexPaths.insert(indexPath) //select
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.accessoryType = selectedIndexPaths.contains(indexPath) ? .checkmark : .none
}

Model
class Item {
var name:String!
var isSelected = false
}
then inside cellForRowAt
cell.accessoryType = items[indexPath.row].isSelected ? .checkmark : .none
inside didSelectRowAt
items[indexPath.row].isSelected.toggle()
tableView.reloadRows(at:[indexPath],with:.none)

Set isSingleSelection to false for multiple selections and to true for single selection
var isCellSelected = [Bool]()
var isSingleSelection = false
override func viewDidLoad() {
super.viewDidLoad()
for _ in arrayFillingYourTableview { isCellSelected.append(false) }
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = branchTbView.dequeueReusableCell(withIdentifier: "cell")!
cell.accessoryType = isCellSelected[indexPath.row] ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if isSingleSelection {
for i in 0..<isBranchSelected.count {
isCellSelected[i] = false
}
}
isCellSelected[indexPath.row] = ! isCellSelected[indexPath.row]
tableView.reloadData()
}
}

Related

Getting label text when Cell is Selected in TableView (Swift)

I have the below code and I want to print the text of the selected cell ( a custom cell with a text label )
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DateCell", for: indexPath) as! DateCell
cell.dateLabel.text = objectsArray[indexPath.section].sectionObjects[indexPath.row]
cell.selectionStyle = .none
contentView.separatorStyle = .singleLine
contentView.allowsSelection = true
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if contentView.cellForRow(at: indexPath)?.accessoryType == UITableViewCell.AccessoryType.none{
contentView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
else{
contentView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
}
I already tried adding the below code in didSelect row at but I get nil.
print ((contentView.cellForRow(at: indexPath)?.textLabel?.text)!)
Any ideas on how I can do this?
Get it from the original source...
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
let str = objectsArray[indexPath.section].sectionObjects[indexPath.row]
print(str)
}
Since you are setting text from the data source, when cell is selected, you can check the index in your data source
if let text = objectsArray[indexPath.section].sectionObjects[indexPath.row]{
//Do Something
}

How to convert multiple cell selection to single cell selection using Swift

In myscenario, I am trying to create single cell selection checkmark at a time. I used below code for multiple cell selection with isSelected Bool value for selection cell persistent. Now, how to convert below code for single cell selection.
My Code Below
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let item = self.titleData[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = item.isSelected ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
titleData[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
let selectedTitle = titleData.filter{$0.isSelected}
print("\(selectedTitle)")
}
First, in viewDidLoad(), make your tableView to allow single selection only. like this:
yourTableView.allowsMultipleSelection = false
then you can use didSelectRowAt and didDeselectRowAt for this. This will enable only one selection at a time.
// assign isSelected true and accessoryType to checkmark
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
self.titleData[indexPath.row].isSelected = true
let selectedTitle = self.titleData[indexPath.row].title
cell.accessoryType = .checkmark
}
// assign isSelected false and accessoryType to none
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
self.titleData[indexPath.row].isSelected = false
cell.accessoryType = .none
}
You need to maintain global variable because if you want to manage using your array you need to reset isSelected bit of array every time before you do selection.
var isSelected = false
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let item = self.titleData[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = isSelected ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
isSelected = true
tableView.reloadData()
}

How to have checkmarks icons (accessory view) on tableview already checked after cells rendered?

I have model data, which has a boolean value that says if checkmark should be shown (accessoryType is .checkmark)...
So for example, I want to have two of five rows checkmarked at start (based on my model as I said)... The thing is, I am able to show checkmarks, but after I tap on them, toggling doesn't work right:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
model[indexPath.row].isCellSelected = true
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
model[indexPath.row].isCellSelected = false
}
}
And here is a cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = model[indexPath.row]
let identifier = data.subtitle != nil ? kSubtitleID : kNormalID
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = data.title
cell.detailTextLabel?.text = data.subtitle
return cell
}
I am able to show check mark like this:
cell.accessoryType = data.isCellSelected ? .checkmark : .none
But when I tap on it, it cause it is selected (allowsMultipleSelection is set to true), it doesn't get toggled, but rather stays for the first time.
Here is the model I use. It is really simple:
struct CellModel{
var title:String?
var subtitle:String?
var isCellSelected:Bool = false
}
You should perform toggling in tableView:didSelectRowAt: only, and reload the necessary cell afterwards. You should omit tableView:didDeselectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
// Toggle the value - true becomes false, false becomes true
model[indexPath.row].isCellSelected = !model[indexPath.row].isCellSelected
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .none)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = model[indexPath.row]
let identifier = data.subtitle != nil ? kSubtitleID : kNormalID
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = data.title
cell.detailTextLabel?.text = data.subtitle
// Set the checkmark or none depending on your model value
cell.inputAccessoryType = data.isCellSelected ? .checkmark : .none
return cell
}
Edit:
Use this for single selection only + ability to deselect selected item:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Loop through all model items
for (index, item) in model.enumerated() {
if (index == indexPath.row) {
// Toggle tapped item
item.isCellSelected = !item.isCellSelected
} else {
// Deselect all other items
item.isCellSelected = false
}
}
tableView.reloadData();
}
You dont have to use select and deselect until you have a model that saves the boolean value
In tableview didselect method
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if model[index.row].isCellSelected == true {
model[indexpath.row].isCellSelected = false
}
else {
model[indexpath.row].isCellSelected = true
}
tableview.reloadData
}
And in cell for row check
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if model[index.row].isCellSelected{
cell.accessoryType = .checkmark
}
else {
cell.accessoryType = .none
}
}
What I did to make all work is:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let data = model[indexPath.row]
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
model[indexPath.row].isCellSelected = true
self.selectedData?(model.filter{$0.isCellSelected})
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let data = model[indexPath.row]
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
model[indexPath.row].isCellSelected = false
self.selectedData?(model.filter{$0.isCellSelected})
}
}
and
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = model[indexPath.row]
let identifier = data.subtitle != nil ? kSubtitleID : kBasicID
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = data.title
cell.detailTextLabel?.text = data.subtitle
cell.accessoryType = data.isCellSelected ? .checkmark : .none
return cell
}
but I had to initially setup which cells are selected actually:
func setSelectedCells(){
let selectedIndexes = self.model.enumerated().compactMap { (index, element) -> IndexPath? in
if element.isCellSelected{
return IndexPath(row: index, section: 0)
}
return nil
}
selectedIndexes.forEach{
selectRow(at: $0, animated: false, scrollPosition: .none)
}
}
and then called this in viewDidAppear, cause I had to be sure that table has drawn its content (cause this will fail if we try something (cell) that doesn't exist yet). Not a best way, but it solved the issue for single and multiple selections with initial states.
Then, cellForRowAt method becomes as simple as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = model[indexPath.row]
let identifier = data.subtitle != nil ? kSubtitleID : kNormalID
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = data.title
cell.detailTextLabel?.text = data.subtitle
cell.accessoryType = data.isCellSelected ? .checkmark : .none
return cell
}
Then, didSelectRowAt method becomes as simple as:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
let isCellSelected = !(model[indexPath.row].isCellSelected)
cell.accessoryType = isCellSelected ? .checkmark : .none
model[indexPath.row].isCellSelected = isCellSelected
}
}
Note we had to toggle the flag and update the cell and the model based on the new value.
Even better, you can replace the .accessory type updating line with:
tableView.reloadRows(at: [indexPath], with: .none)
So the method would look like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
let isCellSelected = !(model[indexPath.row].isCellSelected)
model[indexPath.row].isCellSelected = isCellSelected
tableView.reloadRows(at: [indexPath], with: .none)
}
}
Note that we update the model, then we reload the cell that will cause cellForRowAt to be called, and that method takes care on proper configurations based on model.
More generally, I'd recommend to use some StateController object to keep the state for each cell / row and take care of the toggling.
I've wrote a whole article a while ago that does exactly what you need to do, and showcases a lot of best practices too:
https://idevtv.com/how-to-display-a-list-of-items-in-ios-using-table-views/
#Whirlwind, Hi its not so complicate if you need to show selections only inside the tableView, here i am delivering my ans after your model update.
This can be done by maintaining isCellSelected property in didSelectRowAt only.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
model[indexPath.row].isCellSelected = model[indexPath.row].isCellSelected ? false : true
tableView.reloadData()
}
Here is cellForRowAt you can also alter some more lines here.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = model[indexPath.row]
let identifier = "kNormalID"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
cell.textLabel?.text = data.title
cell.detailTextLabel?.text = data.subtitle
cell.accessoryType = data.isCellSelected ? .checkmark : .none
return cell
}
Hope i delivered you my best. Let me know if i missed anything.
Why don't you try following!
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
model[indexPath.row].isCellSelected = false
tableView.reloadRows(at: [indexPath], with: <UITableViewRowAnimation>)
}
}

How to deselect a row when another row tapped?

Here is what I want to archive: deselect the "English" row when the second Chinese row tapped, or conversely.
I want only one checkmark to be shown, not all of them. Here is my code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath)!.accessoryType == .checkmark {
tableView.cellForRow(at: indexPath)!.accessoryType = .none
navigationItem.rightBarButtonItem!.isEnabled = false
} else if tableView.cellForRow(at: indexPath)!.accessoryType == .none {
tableView.cellForRow(at: indexPath)!.accessoryType = .checkmark
navigationItem.rightBarButtonItem!.isEnabled = true
}
}
Finally, I got an answer. Here you go:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)!.accessoryType = .checkmark
navigationItem.rightBarButtonItem?.isEnabled = true
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)!.accessoryType = .none
navigationItem.rightBarButtonItem?.isEnabled = false
}

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