Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift indexPathsForSelectedRows returns nil

Tags:

I am working on a tableView, goal is to implement a expandible/collapsible tableView cells. The number of cells is completely dynamic.

I have things working, but with a small problem. I am using didSelectRowAtIndexPath to toggle expand/collapse cell. So what happens now is the toggle executes by tapping anywhere on the cell. :-(

Inside the cell, I have two UIViews (Header and Detail precisely). I need the toggle expand/collapse to work only when I tap on Header and not the whole cell.

For that, I am fetching the indexPath like here but that doesn't work since indexPathsForSelectedRows returns nil.

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }


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

    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        if selectedIndex == indexPath.row {
            return 150.0
        }
        else {
            return 40.0
        }
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        cell = tableView.dequeueReusableCellWithIdentifier("CollapsedCell", forIndexPath: indexPath) as! CollapsedCell

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(VoucherDetailViewController.expandCell(_:)))
        (cell as! CollapsedCell).headerView.addGestureRecognizer(tapGestureRecognizer)

        return cell
    }

    func getIndexPathForSelectedCell() -> NSIndexPath? {

        var indexPath: NSIndexPath?

        if tableView.indexPathsForSelectedRows?.count > 0 {   //**nil is returned here**
            indexPath = (tableView.indexPathsForSelectedRows?[0])! as NSIndexPath
        }
        return indexPath
    }

    func expandCell(sender: UITapGestureRecognizer) {
        if let myIndexPath = getIndexPathForSelectedCell() {
            if selectedIndex == myIndexPath.row {
                selectedIndex = -1
            }
            else {
                selectedIndex = myIndexPath.row
            }

            self.tableView.beginUpdates()
            self.tableView.reloadRowsAtIndexPaths([myIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
            self.tableView.endUpdates()
        }
    }

Could someone give me a clue why the indexPathsForSelectedRows returns nil?

Many Thanks!

UPDATE 1: Current Working demo

When I add my expand cell logic in didSelectRowAtIndexPath and remove my tapGestureRecognizer for the view inside the cell - it looks like this -

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

        if selectedIndex == indexPath.row {
            selectedIndex = -1
        }
        else {
            selectedIndex = indexPath.row
        }

        self.tableView.beginUpdates()
        self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
        self.tableView.endUpdates()
    }
like image 748
Lohith Korupolu Avatar asked Jun 07 '17 10:06

Lohith Korupolu


2 Answers

You got this bug because

self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)

reloads and deselect your rows.

You just select row, after that, you reload row (this action will deselect this row) and later you want to get a selected rows using

tableView.indexPathsForSelectedRows

but table view does not have any selected rows at this moment.

As a solution, you can store selected indexPath in new variable, and use this variable instead of

tableView.indexPathsForSelectedRows
like image 117
Vlad Khambir Avatar answered Sep 21 '22 11:09

Vlad Khambir


From the indexPathsForSelectedRows documentation:

The value of this property is nil if there are no selected rows.

Therefore:

guard let selectedRows = tableView.indexPathsForSelectedRows else {
   return nil
}

return selectedRows[0]

(no check for count is needed, the method never returns an empty array).

By the way, you could simplify the condition using:

return tableView.indexPathsForSelectedRows?.first

or, if your table does not support multiple selection, directly call:

return tableView.indexPathForSelectedRow

which is already there.

like image 33
Sulthan Avatar answered Sep 22 '22 11:09

Sulthan