Restricts multiple selection in a tableView with a stepper (Swift) - ios

I have two viewControllers, the first with a simple stepper
#IBAction func stepper(_ sender: UIStepper) {
restrictioNumber.text = String(sender.value)
}
and the second with a tableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "CATEGORY_CELL"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
cell.textLabel?.text = list[indexPath.row].name
return cell
}
let nearbySearchSegueIdentifier = "goToMcourse"
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = .checkmark
/* self.performSegue(withIdentifier: nearbySearchSegueIdentifier, sender: list[indexPath.row]) */
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = .none
}
in this tableView i can select multiple cells but i would like to make a limit in how many can be selected and this limit must be represented by the number set with the stepper in the previous viewController
How i can do this?

Related

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

TableView CheckMark and Uncheck With Scroll Up Still Checked Cell Value In Ios Swift 4

TableView CheckMark Cell Value Removed After Scrolling Up It will Fix
TableView in You have face a problem many times to Checkmark after scroll Up then Scroll Down To show a Your Checkmark cell is will Removed Because cell is dequeueReusableCell So This Problem Fix , you Have just put Your code and Solved Your Problem.
Any More Help So Send Massage.
Thank you So much. :)
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate{
var temp = [Int]()
var numarr = [Int]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numarr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "id")
cell = UITableViewCell.init(style: .default, reuseIdentifier: "id")
cell?.textLabel?.text = String(numarr[indexPath.row])
if temp.contains(numarr[indexPath.row] as Int)
{
cell?.accessoryType = .checkmark
}
else
{
cell?.accessoryType = .none
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if temp.contains(numarr[indexPath.row] as Int)
{
cell?.accessoryType = .none
temp.remove(at: temp.index(of: numarr[indexPath.row])!)
}
else
{
cell?.accessoryType = .checkmark
temp.append(self.numarr[indexPath.row] as Int)
}
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...100
{
numarr.append(i)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
I think if someone were to run your code it would not show any error. But with real data it probably will. The reason is the way you store your checkmarks. You store the data of a row into the temp array when you should be storing the actualy indexPath of the array so that only that row gets the checkmark. In your case, if a row has 1 inside it's label and you click on it, that cell will be highlighted. Now if you start scrolling and another cell contains 1 then that row will also be highlighted.
I have modified your example for the case of a single section. If there is more than one section, you need to store the indexPath instead of indexPath.row.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "id")
cell = UITableViewCell.init(style: .default, reuseIdentifier: "id")
cell?.textLabel?.text = String(numarr[indexPath.row])
if temp.contains(indexPath.row) {
cell?.accessoryType = .checkmark
} else {
cell?.accessoryType = .none
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if temp.contains(indexPath.row) {
cell?.accessoryType = .none
temp.remove(at: indexPath.row)
} else {
cell?.accessoryType = .checkmark
temp.append(indexPath.row)
}
}
You are strongly discouraged from using a second array to keep the selected state.
This is Swift, an object oriented language. Use a custom struct for both num and the selected state.
In didSelectRowAt and didDeselectRowAt change the value of isSelected and reload the row.
And use always the dequeueReusableCell API which returns a non-optional cell.
struct Item {
let num : Int
var isSelected : Bool
}
var numarr = [Item]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numarr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "id", for: indexPath)
let item = numarr[indexPath.row]
cell.textLabel?.text = String(item)
cell.accessoryType = item.isSelected ? .checkmark : .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
updateSelection(at: indexPath, value : true)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
updateSelection(at: indexPath, value : false)
}
func updateSelection(at indexPath: IndexPath, value : Bool) {
let item = numarr[indexPath.row]
item.isSelected = value
tableView.reloadRows(at: [indexPath], with: .none)
}
override func viewDidLoad() {
super.viewDidLoad()
(0...100).map{Item(num: $0, isSelected: false)}
}

Multiple checkmark in Tableview in swift3

How to do the multiple checkmark in tableview. I need to select the multiple checkmark in tableview and what are the checkmarks I need to select to place the multiple values in label.
Example player1,player2,player3 in label
here is my code
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return TypeOfAccountArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! UITableViewCell
let cell:TypeofAccountCell=tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TypeofAccountCell
cell.Uertype_lbl.text=TypeOfAccountArray[indexPath.row]
cell.selectionStyle = UITableViewCellSelectionStyle.none;
cell.Uertype_lbl.font = UIFont(name:"Roboto-Regular", size:13)
cell.Uertype_lbl.adjustsFontSizeToFitWidth = true
if (selectedIndex == indexPath as NSIndexPath?) {
cell.checkmarkbtn.setImage(UIImage(named: "checkmark.png"),for:UIControlState.normal)
} else {
cell.checkmarkbtn.setImage(UIImage(named: "uncheckmark.png"),for:UIControlState.normal)
}
// Configure the cell...
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
let row = indexPath.row
print(TypeOfAccountArray[row])
selectedIndex = indexPath as NSIndexPath?
self.Type_of_account_txt.text = (TypeOfAccountArray[row])
self.Type_account_view.isHidden = true
tableView.reloadData()
}
Change your selectedindex to hold array of index path var selectedIndexes = [IndexPath](), on your cell xib, set your checkmark image on button selected stated and uncheckmark image on normal status and use the below code.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TypeOfAccountArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TypeofAccountCell=tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TypeofAccountCell
cell.Uertype_lbl.text=TypeOfAccountArray[indexPath.row]
cell.selectionStyle = UITableViewCellSelectionStyle.none;
cell.Uertype_lbl.font = UIFont(name:"Roboto-Regular", size:13)
cell.Uertype_lbl.adjustsFontSizeToFitWidth = true
// Configure the cell...
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
let cell:TypeofAccountCell=tableView.cellForRow(at: indexPath) as! TypeofAccountCell
if selectedIndexes.contains(indexPath)
{
cell.checkmarkbtn.isSelected = false
if let index = selectedIndexes.index(of: indexPath) {
selectedIndexes.remove(at: index)
}
}
else
{
cell.checkmarkbtn.isSelected = true
selectedIndexes.append(indexPath)
}
}
self.Type_of_account_txt.text = ""
for element in selectedIndexes
{
self.Type_of_account_txt.text = (self.Type_of_account_txt.text ?? "") + "\(TypeOfAccountArray[element.row]) ,"
}
if (selectedIndexes.count > 0)
{
self.Type_of_account_txt.text = self.Type_of_account_txt.text?.substring(to: (self.Type_of_account_txt.text?.index(before: (self.Type_of_account_txt.text?.endIndex)!))!)
}
}
you need to follow this step :
In didSelectRowAt, you need to add and remove indexpath in array for multiple checkmark.
Now , in cellForRowAtIndexPath you need to check that current
indexPath consist in array .
if (![arrIndexPath containsObject: indexPath]) {
// do something
cell.checkmarkbtn.setImage(UIImage(named: "checkmark.png"),for:UIControlState.normal)
}

Link the UITableViewCell label to next ViewController programmatically

I have created 2 TableViewControllers in one ViewController programmatically as :
// contents for labels in cells for tableviewcontrollers
let contents1 : [String] = ["One:","Two:","Three:","Four:","Five:"]
let contents2 : [String] = ["Six:","Seven:","Eight:","Nine:","Ten:"]
override func viewDidLoad() {
super.viewDidLoad()
table1.delegate = self
table1.dataSource = self
table2.delegate = self
table2.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(tableView.tag == 1)
{
return contents1.count
}
else if (tableView.tag == 2)
{
return contents2.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath )
if (tableView.tag == 1)
{
cell.textLabel?.text = contents1[indexPath.row]
}
else if (tableView.tag == 2)
{
cell.textLabel?.text = contents2[indexPath.row]
}
return cell
}
My question is that , how can I programmatically link "Four:" label of first TableViewController "table1" when selected to show up next new ViewController without using Segue?
You can UITableView's deSelectRowAt delegate method to identify the cell selection after that you can proceed to next view
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if cell?.textLabel?.text == "Four" { // you can use you indexPath to compare as well
let secondViewController = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = pets[indexPath.row]
// Configure the cell...
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
performSegue(withIdentifier: "segue", sender: self)

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