48

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




    }
//----------------------------------------------------------------------------------------

}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Jp4Real
  • 1,982
  • 4
  • 18
  • 33

15 Answers15

80

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
}
Saqib Omer
  • 5,387
  • 7
  • 50
  • 71
Roger
  • 865
  • 8
  • 9
37

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
            }
       }
   }
}
Dejan Skledar
  • 11,280
  • 7
  • 44
  • 70
  • 1
    thanks a lot it doesn't bring an error but no checkmark shows, – Jp4Real May 05 '15 at 17:50
  • I found that i made a mistake copying it, when I put it in it shows an error for cell.accessoryType = row == tells me that row is an unresolved identifier.... i knows it works with row since my code above has it, – Jp4Real May 05 '15 at 19:07
  • Sure, whats your question? – Dejan Skledar May 05 '15 at 19:15
  • the checkboxes disappear when i scroll the list down... anyway i can solve this? – Jp4Real May 05 '15 at 19:29
  • You are probally setting the accessoryType in the cellForRowAtIndexPath, try deleting that row. – Dejan Skledar May 05 '15 at 19:30
  • for the reset thanks ! but should i put this code in IBAction Reset: UIButton?
    as for the accessory i don't get which row you want me to delete?
    – Jp4Real May 05 '15 at 19:45
  • the row, in which you set the accessoryType, for example "cell.accessoryType = .None", in the cellForRowAtIndexPath method – Dejan Skledar May 05 '15 at 19:54
  • I'll add my view controller.swift code to my original answer for guidance cause i dont know what you want me to delete – Jp4Real May 05 '15 at 20:16
  • i've deleted the row and the checkmarks still get deleted when i scroll and the reset gives me an error for (IndexPath) unresolved identifier – Jp4Real May 06 '15 at 19:01
  • Awesome ! the reset button works ! I'm still working on those chekmarks disappearing... i'll let you know if i find anything – Jp4Real May 06 '15 at 19:38
  • You will likely need to implement the same if statement in didDeselectRowAtIndexPath too – Ace Green Jun 26 '15 at 17:32
  • The check marks disappear when scrolling and also they appear on different rows as well. – Vakas Sep 01 '15 at 11:13
  • I get: fatal error: Index out of range on row: if !checked[indexPath.row] { – SDW Jun 17 '16 at 12:47
  • 1
    Better updating the model like let isChecked = checked[indexPath.row]; checked[indexPath.row] = !isChecked in didSelectRowAtIndexPath and call reloadRowsAtIndexPaths. – Andrei Marincas Aug 01 '16 at 12:05
  • 1
    A `Set` is a better data structure than an array of `Bool` – Paulw11 Nov 30 '16 at 22:20
25

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.

n13
  • 6,843
  • 53
  • 40
19

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
        }
    }   
}
Benjamin Baron
  • 158
  • 1
  • 6
AnupamChugh
  • 1,779
  • 1
  • 25
  • 36
13

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
    }
}
Wilson
  • 386
  • 3
  • 4
6

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

}

Brian Bird
  • 1,176
  • 18
  • 21
  • This answer ensures that when the tableview is being scrolled, only the user selected Cell / Index would have the checkmark on it. Otherwise, when scroll is happening, the checkmarks will appear in multiple (not selected) locations. the key is at the comment `//configure your cell here` Unfortunately, I missed this nugget and had to do my head banging to figure it out myself. – app4g Jan 12 '21 at 14:37
3

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
            }
        }
    }
}
Kinergy
  • 325
  • 3
  • 9
3

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
        }
    }
}
fs_tigre
  • 10,650
  • 13
  • 73
  • 146
1

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
}
M Murteza
  • 1,629
  • 15
  • 10
1

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
    }
}
Nish
  • 545
  • 5
  • 7
1

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
}
Harshad Kale
  • 17,446
  • 7
  • 30
  • 44
1

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
                }
           }
       }
    }
husharoonie
  • 431
  • 1
  • 4
  • 19
0
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
    }
}
pckill
  • 3,709
  • 36
  • 48
0

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
    }
}
Wilson Desimini
  • 722
  • 5
  • 10
0

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
         }
     }
     
     
 }
gcharita
  • 7,729
  • 3
  • 20
  • 37