-1

It is posible to add a select all option in TableView Multiple checkmarks...?

I have a code ready, and I try to do this function. please help me !

CODE:

struct Area {
        let name : String
        var isSelected : Bool

        init(name : String, isSelected : Bool = false) {
            self.name = name
            self.isSelected = isSelected
        }
    }

    var areas = [Area(name: "Select all"),Area(name: "a"),Area(name: "b"),Area(name: "c"),Area(name: "d"]

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return areas.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let area = areas[indexPath.row]
        cell.textLabel?.text = area.name
        cell.accessoryType = area.isSelected ? .checkmark : .none

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        areas[indexPath.row].isSelected.toggle()
        tableView.reloadRows(at: [indexPath], with: .none)
        let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
        print(selectedAreas)

    }

Thanks.

Iti Fjfj
  • 11
  • 5
  • Possible duplicate of [iOS: Tableview multiple selection - AccessoryCheckmark checking reusable cells](https://stackoverflow.com/questions/10216597/ios-tableview-multiple-selection-accessorycheckmark-checking-reusable-cells) – えるまる Sep 18 '19 at 07:37

5 Answers5

1

First you need to enable multiple selection for the table view

tableView.allowsMultipleSelection = true
tableView.allowsMultipleSelectionDuringEditing = true

Select all rows

for section in 0..<tableView.numberOfSections {
        for row in 0..<tableView.numberOfRows(inSection: section) {
            let indexPath = IndexPath(row: row, section: section)
            tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
        }
    }

To enable checkmark whenever a row is selected:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) {
        let area = areas[indexPath.row]
        area.isSelected = true
        cell.accessoryType = .checkmark
    }
}

Also remove checkmark when it is deselected:

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) {
        let area = areas[indexPath.row]
        area.isSelected = false
        cell.accessoryType = .none
    }
}

Please vote this answer if it helps. Thanks.

Duy Le
  • 87
  • 3
  • Works great!! Maybe you can help me if you know how to add a search option? I will mark as the correct answer !! – Iti Fjfj Sep 18 '19 at 08:34
1

You have to check if the user selected the first row ("Select all") and update the other rows accordingly:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // toggle the selected area
    areas[indexPath.row].isSelected.toggle()

    // save the new state for later use
    let isSelected = areas[indexPath.row].isSelected

    if indexPath.row == 0 {
        // "Select all" was selected – update all areas
        for i in 1..<areas.count {
            areas[i].isSelected = isSelected
        }

        // update UI
        tableView.visibleCells.forEach { $0.accessoryType = isSelected ? .checkmark : .none }
    } else {
        // update UI
        tableView.cellForRow(at: indexPath)?.accessoryType = isSelected ? .checkmark : .none
    }

    tableView.deselectRow(at: indexPath, animated: true)
}

Recommendation

To separate concerns visually you could also use an own table view section for the "Select all" row. In that case some more changes are necessary:

var areas = [
    // you do not need an area for "Select all" any longer
    Area(name: "a"),
    Area(name: "b"),
    Area(name: "c"),
    Area(name: "d")
]

var allSelected: Bool {
    // helper var to see if all areas are currently selected
    return areas.filter({!$0.isSelected}).isEmpty
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    switch section {
    case 1: return "Areas"
    default: return nil
    }
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    switch section {
    case 0: return 1 // select all
    case 1: return areas.count
    default:
        // we should never get here
        fatalError()
    }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.selectionStyle = .none

    if indexPath.section == 0 {
        cell.textLabel?.text = "Select all"
        cell.accessoryType = allSelected ? .checkmark : .none
    } else {
        let area = areas[indexPath.row]
        cell.textLabel?.text = area.name
        cell.accessoryType = area.isSelected ? .checkmark : .none
    }

    return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.section == 0 {
        // (de-)select all
        let shouldSelect = !allSelected
        for i in 0..<areas.count {
            areas[i].isSelected = shouldSelect
        }
    } else {
        areas[indexPath.row].isSelected.toggle()
    }

    tableView.reloadRows(at: tableView.indexPathsForVisibleRows ?? [], with: .automatic)
}
André Slotta
  • 13,774
  • 2
  • 22
  • 34
0

First change your Area to class so it will be reference type

    class Area {
        let name : String
        var isSelected : Bool

        init(name : String, isSelected : Bool = false) {
            self.name = name
            self.isSelected = isSelected
        }
    }

Now change your didselect logic

       func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let area = areas[indexPath.row]
        area.isSelected.toggle()
        if area.name == "Select all"{
          areas = areas.map{$0.isSelected = true}
        }
        tableView.reloadRows(at: [indexPath], with: .none)
        let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
        print(selectedAreas)

     }
Abhi Yaduwanshi
  • 380
  • 3
  • 12
0

In didSelectRow:

if indexPath.row == 0 { // the first item is the select all button
    // find out which areas should be selected
    let indicesThatShouldBeSelected = (1..<areas.count).filter { !areas[$0].isSelected }
    // update model
    indicesThatShouldBeSelected.forEach { areas[$0].isSelected = true }
    // update view
    tableView.reloadRows(at: indicesThatShouldBeSelected.map { IndexPath(section: 0, row: $0) }, with: .none)
}

You could also just do tableView.reloadData(), which reloads the whole table view instead of just the rows that needs reloading.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

If the selectAll row is at index 0 check the row, then set all isSelected members to true and reload the entire table

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.row == 0 {
        areas.indices.forEach{areas[$0].isSelected = true}
        tableView.reloadData()
    } else {
        areas[indexPath.row].isSelected.toggle()
        tableView.reloadRows(at: [indexPath], with: .none)
    }
    let selectedAreas = areas.filter{$0.isSelected}.map{$0.name}
    print(selectedAreas)

}

And if you want to exclude the first item to be selected drop the first index

areas.indices.dropFirst().forEach{areas[$0].isSelected = true}
vadian
  • 274,689
  • 30
  • 353
  • 361