Badr Bujbara
Badr Bujbara

Reputation: 8671

iOS Swift: Maintaining Toggle Button State In A Cell

I have a button in a cell as a toggle to check in members in a club. When I check in a member, I need the button's state to stay ON after scrolling, but it turns back off. Here is the cellForRow method:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = self.membersTableVw.dequeueReusableCell(withIdentifier: "CellMembersForCoach", for: indexPath) as! CellMembersForCoach
    let member = members[indexPath.row]
    cell.setMember(member)
    cell.cellController = self
    return cell
}

Here is the portion in the custom cell class where I toggle the button

@IBOutlet weak var checkBtn: UIButton!
@IBAction func setAttendance(_ sender: Any){
    // toggle state
    checkBtn.isSelected = !checkBtn.isSelected
}

The toggling works but after scrolling the table, the button state changes back to original. Any suggestion is appreciated.

Upvotes: 0

Views: 1890

Answers (5)

Badr Bujbara
Badr Bujbara

Reputation: 8671

Shamas highlighted a correct way to do it, so I'll share my whole solution.

I created a singleton class to store an array of checked cells:

class Utility {

// Singleton
private static let _instance = Utility()
static var Instance: Utility{
    return _instance
}

 var checkedCells = [Int]()

In the custom cell class I have action method wired to the check button to add and remove checked cells:

@IBOutlet weak var checkBtn: UIButton!
@IBAction func setAttendance(_ sender: Any){
    // Get cell index
    let indexPath :NSIndexPath = (self.superview! as! UITableView).indexPath(for: self)! as NSIndexPath

    if !checkBtn.isSelected{
       Utility.Instance.checkedCells.append(indexPath.row)
    }else{
        // remove unchecked cell from list
        if let index = Utility.Instance.checkedCells.index(of: indexPath.row){
            Utility.Instance.checkedCells.remove(at: index)
        }
    }
    // toggle state
    checkBtn.isSelected = !checkBtn.isSelected
}

In the cellForRowAt method in the view controller I check if the cell row is in the array and decide if the toggle button should be checked:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = self.membersTableVw.dequeueReusableCell(withIdentifier: "CellMembersForCoach", for: indexPath) as! CellMembersForCoach
    if Utility.Instance.checkedCells.contains(indexPath.row){
        cell.checkBtn.isSelected = true
    }
    return cell
}

Upvotes: 1

Vikky
Vikky

Reputation: 936

Since the tableView is reusing cells you code is not going to work. You have to keep track of each button when selected and set it again when tableview is reusing cells when you scroll. Solution : You can take an array(contains bool) which is size of your tableview data. So you have to set state of button using array and update array when selected or deselected.

Upvotes: 0

Jaykumar Patel
Jaykumar Patel

Reputation: 684

This is because of tableview is reusing your cell. so you have to maintain button as per tableView data source.

Upvotes: 3

ngbaanh
ngbaanh

Reputation: 535

The problem is here:

checkBtn.isSelected = !checkBtn.isSelected

This code will reflect the button selection state every time the cell is configured when the delegate cellForRowAt invokes. So if you selected it before, now it turns to not-selected.

Upvotes: 0

Shamas S
Shamas S

Reputation: 7549

This happens because you are reusing the cells.

You need to keep track of which cells have been selected. Perhaps in your member's class. Then when you are in your cellForRowAt you should check if this cell has been selected before and set the correct state for your button.

Upvotes: 4

Related Questions