Select ALL TableView Rows Programmatically Using selectRowAtIndexPath

13,682

Solution 1

At the time allJobsSelected becomes true, you need to call the UITableView method selectRowAtIndexPath(_:animated:scrollPosition:) for each row of your table. In my case, I attached this functionality to the right bar button item which I named Select All. Calling this from cellForRowAtIndexPath is surely not the right place.

@IBAction func doSelectAll(sender: UIBarButtonItem) {
    let totalRows = tableView.numberOfRowsInSection(0)
    for row in 0..<totalRows {
        tableView.selectRowAtIndexPath(NSIndexPath(forRow: row, inSection: 0), animated: false, scrollPosition: UITableViewScrollPosition.None)
    }
}

Solution 2

For Swift 3 and answering your question literally, regardless of your code.

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

If you want to inform the tableview delegate, use this method:

func selectAllRows() {
    for section in 0..<tableView.numberOfSections {
        for row in 0..<tableView.numberOfRows(inSection: section) {
            let indexPath = IndexPath(row: row, section: section)
            _ = tableView.delegate?.tableView?(tableView, willSelectRowAt: indexPath)
            tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
            tableView.delegate?.tableView?(tableView, didSelectRowAt: indexPath)
        }
    }
}

Solution 3

Functional solution (Swift 5.1)

extension UITableViewDataSource where Self: UITableView {
  /**
   * Returns all IndexPath's in a table
   * ## Examples:
   * table.indexPaths.forEach {
   *    selectRow(at: $0, animated: true, scrollPosition: .none) // selects all cells
   * }
   */
  public var indexPaths: [IndexPath] {
     return (0..<self.numberOfSections).indices.map { (sectionIndex: Int) -> [IndexPath] in
        (0..<self.numberOfRows(inSection: sectionIndex)).indices.compactMap { (rowIndex: Int) -> IndexPath? in
           IndexPath(row: rowIndex, section: sectionIndex)
        }
        }.flatMap { $0 }
  }
}
Share:
13,682
Tom
Author by

Tom

Updated on July 14, 2022

Comments

  • Tom
    Tom almost 2 years

    I'm trying to programmatically select all rows in my tableview using the following code:

     func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell:myTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! myTableViewCell
    
         cell.accessoryType = .None
    
        if allJobsSelected {
    
            let bgColorView = UIView()
            bgColorView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
            cell.contentView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
            cell.selectedBackgroundView = bgColorView
            cell.accessoryType = .Checkmark
            cell.highlighted = false
    
            cell.selected = true
            //  cell.accessoryType = .Checkmark
            self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
            self.tableView(self.tableView, didSelectRowAtIndexPath: indexPath)
    
        }
    
        var job: Jobs!
    
        job = jobs[UInt(indexPath.row)] as! Jobs
    
        cell.reports2JobTitle.text = job.jobTitle
    
    
        return cell
    }
    
     func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    
        self.tableView.allowsMultipleSelection = true
    
        if let cell:myTableViewCell = tableView.cellForRowAtIndexPath(indexPath) as? myTableViewCell {
    
            let bgColorView = UIView()
            bgColorView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
            cell.contentView.backgroundColor = UIColor(red: 250/255, green: 182/255, blue: 17/255, alpha: 1)
            cell.selectedBackgroundView = bgColorView
            cell.accessoryType = .Checkmark
            cell.highlighted = false
            self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.Bottom)
    
        }
    
    
    }
    

    My issue is that only the rows that have been dequeued are added to my table's data model when I segue to the next viewcontroller. In order to add all the rows to my table's data model I have to manually scroll through the whole table. How can I change this so all the selected rows are added to my table's data model without having to scroll through the whole table?

    What I cannot understand is that after all my rows are selected I then loop through my indexPaths as follows but not all of the indexPaths are added unless I first scroll through the entire table.

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    
        if (segue.identifier == "reportsDisplay") {
            let controller = segue.destinationViewController as! ReportsDisplayViewController
    
            var selectedJob : Jobs!
    
            if let indexPaths = tableView.indexPathsForSelectedRows {
    
    
                for i in 0 ..< indexPaths.count {
    
                    let thisPath = indexPaths[i]
    
    
    
                    selectedJob = jobs[UInt(thisPath.row)] as! Jobs
    
    
    
                    let jobTitle = selectedJob.jobTitle
                    let id = selectedJob.identifier
    
    
    
                    jobsToReport.append(jobTitle)
                    jobsID.append(id)
    
    
    
                }
    
    
            }
    
            controller.reportedJobs = jobsToReport
            controller.idOfJobs = jobsID
    
        }
    
    
    }
    
  • Tom
    Tom almost 8 years
    You're right, I do have gaps in my knowledge. I have added my prepareForSegue function to illustrate. What I don't understand is that within my prepareForSegue function I loop through my indexPaths and I seem to have to make sure that the cell has been dequeued before it is added to my data model. There must be a cleaner method of all my selected cells getting added without me having to manually scroll through the table.
  • vacawama
    vacawama almost 8 years
    It's not clear from your code when allJobsSelected becomes true, but at the time that happens, you should call selectRowAtIndexPath for every row of your table. That will mark the row selected. It is not necessary to scroll to the row or load the data.
  • Sipho Koza
    Sipho Koza over 6 years
    Brilliant - Thank you for the solution!
  • rommex
    rommex over 2 years
    Intersecting UITableViewDataSource and UITableView is not a good idea