0

How can I select and deselect all the cells from this UITableView with one click?

I'm having problems with this button. So, the idea is that when I click it, all the cells from the UITableView have a checkmark (select), and when I click it again all the cells don't have the checkmark (deselect).

I already achieved that the UIButton change the name after the click (from "Select all" to "Deselect all" and viceversa).

These are the lines I wrote:

let locationItems:[String] = ["China", "United States", "South Africa", "Spain", "Australia", "Brazil", "Colombia", "Nigeria", "India"]
var selectedItemsIndex:Int?
var selectIndicator = true

@IBOutlet var tableViewWithOptions: UITableView!
@IBOutlet var selectAllLabel: UIButton!

@IBAction func selectAllButton(sender: AnyObject) {
    if (selectIndicator == true) {

        if selectAllLabel.titleLabel?.text == "Select all" {
            selectAllLabel.setTitle("Deselect all", forState: UIControlState.Normal)
        }
        selectIndicator = false
    } else {

        if selectAllLabel.titleLabel?.text == "Deselect all" {
            selectAllLabel.setTitle("Select all", forState: UIControlState.Normal)
        }
        selectIndicator = true
    }
    tableViewWithOptions.reloadData()
}

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if let index = selectedItemsIndex {
            let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
            cell?.accessoryType = .None
        }
        let cell = tableView.cellForRowAtIndexPath(indexPath)
        cell?.accessoryType = .Checkmark
    }

Pls, let me know. Thanks!

Joe
  • 87
  • 1
  • 13
  • You need to start by having your data model keep track of which rows are currently selected. – rmaddy Dec 07 '15 at 21:02
  • 1
    You can't track selection state in the cell (using the accessory type), you need some other variable. I recommend an NSMutableIndexSet. Selecting a cell means you add the row to the set and deselecting means remove it. Select all is done via addIndexesInRange and deselect all is just to clear the indexset – Paulw11 Dec 07 '15 at 21:04

2 Answers2

2

Think of your UITableViewCells as simply displays for your data in you dataSource, it's easier to think that they can only present information and to present new information you have to update your data source and reload the tableview.

I see your data source is locationItems which is an array of Strings. If you change the type of the items to a tuple of type String and Bool you can assign the name of a location and a bool to a single element in the array, the bool represents checked (true) or not checked(false)

So change locationItems to:

//out data source, contains an array of Tuples containing a string and a bool
let locationItems:[(String, Bool)] = [("China", true), ("United States",false), ("South Africa", false), ("Spain", true), ("Australia", true), ("Brazil",     false), ("Colombia", true), ("Nigeria", true), ("India", true)]

As you can see above each element in the array contains a String and a Bool, this bool represents whether a country is checked or not.

So to 'check' all of your cells, we'll change the values in the data source and reload the tableView. This will cause cellForRow to be called again and the cells will then have our new data from our updated data source.

@IBAction func selectAllButton(sender: AnyObject) {
    //loop through our data source and change the bool in each element to true
    //to indicate that all locations are checked
    for item in locationItems {
        //access second item in the tuple which is our bool and set it's value to true
        item.1 = true
    }
    tableView.reloadData()
}

So the general gist of how you'd do this is that you edit the data source in which your tableView gets it data from instead of changing the cells directly.

Alex Catchpole
  • 7,156
  • 6
  • 20
  • 29
0

As others pointed out, you will have to maintain a data model for your location and its selection state to easily handle your scenario. You can define a simple model for your location information. Define this model struct within your ViewController class.

Location Model

struct Location {
    var name: String
    var selected: Bool

    init(_ name: String, _ selected:Bool) {
        self.name = name
        self.selected = selected
    }
}

Now that your model is defined, you need to load the initial values for your data source and properties. [Note: You could also use a helper class to populate your data source.]

var selectionStateIndicator = false
var locationItems: [Location] = {
    var _locations = [Location]()
    var locationNames = ["China", "United States", "South Africa", "Spain", "Australia", "Brazil", "Colombia", "Nigeria", "India"]

    for location in locationNames {
        _locations.append(Location(location, false))
    }

    return _locations
}()

Update your cellForRowAtIndexPath method to use the new data model to display the data.

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

    cell.textLabel?.text = self.locationItems[indexPath.row].name
    cell.accessoryType = self.locationItems[indexPath.row].selected ? .Checkmark : .None

    return cell
}

Update your selectAllButton action to use the new model object and update the selection status of each row.

@IBAction func selectAllButton(sender: AnyObject) {
    selectionStateIndicator = !selectionStateIndicator

    for var location in locationItems {
        location.selected = selectionStateIndicator
    }

    tableViewWithOptions.reloadData()
}

Update your didSelectRowAtIndexPath method so that whenever there is an individual row selection, you update the row accordingly.

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let cell = tableView.cellForRowAtIndexPath(indexPath)
    locationItems[indexPath.row].selected = !locationItems[indexPath.row].selected

    if !locationItems[indexPath.row].selected {
        cell?.accessoryType = .None
    } else {
        cell?.accessoryType = .Checkmark
    }
}
R P
  • 1,173
  • 2
  • 11
  • 20