mradzinski
mradzinski

Reputation: 624

indexPathForSelectedRow returns nil although I have a cell programmatically selected

I have a UITableView which I load with some application settings. I need the table to be single-select, and since the table holds settings there might be a chance some cell will be programmatically selected based on the setting enabled status.

Currently, I'm experiencing a weird behaviour where if I programmatically select a cell then indexPathForSelectedRow returns nil (when it should return the index for the cell I selected), thus when I tap on a second cell (to change the currenty selected setting) I end up with two cells selected (even when I set allowMultipleSelection to false in my tableView object).

Here's my code:

override func viewDidLoad() {
    tableView.allowsMultipleSelection = false
}

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

    if let cell = cell {
        // tableObject is an array containing settings model
        let row = tableObject[indexPath.row]
        cell.textLabel!.text = row.settingValue
        if row.selected {
            cell.setSelected(true, animated: false)
            cell.accessoryType = .Checkmark
        }
        cell.tag = row.id
    }

    return cell!
}

override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
    // oldIndex is always nil for the cell I called setSelected in cellForRowAtIndexPath
    if let oldIndex = tableView.indexPathForSelectedRow {
        if let oldCell = tableView.cellForRowAtIndexPath(oldIndex) {
            tableView.deselectRowAtIndexPath(oldIndex, animated: true)
            oldCell.accessoryType = .None           
        }
    }

    if let cell = tableView.cellForRowAtIndexPath(indexPath) {
        cell.setSelected(true, animated: true)
        cell.accessoryType = .Checkmark
    }

    return indexPath
}

override func tableView(tableView: UITableView, willDeselectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
    if let cell = tableView.cellForRowAtIndexPath(indexPath) {
        cell.accessoryType = .None
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
    return indexPath
}

Any idea how I can always have only one cell selected at a time?

Thanks!

Upvotes: 0

Views: 1436

Answers (3)

Ayman Ibrahim
Ayman Ibrahim

Reputation: 1399

Note that calling tableView.reloadData() resets the tableView.indexPathForSelectedRow to nil.

here how you can accomplish table view single selection through tableView.indexPathForSelectedRow :

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.accessoryType = tableView.indexPathForSelectedRow == indexPath ? .checkmark : .none
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        if let visiblePaths = tableView.indexPathsForVisibleRows
        {
            for visibleIndexPath in visiblePaths
            {
                let cell = tableView.cellForRow(at: visibleIndexPath)
                if visibleIndexPath == indexPath
                {
                    cell?.accessoryType = .checkmark
                }
                else
                {
                    cell?.accessoryType = .none
                }
            }
        }
    }

Upvotes: 1

mradzinski
mradzinski

Reputation: 624

Just in case someone else comes across this same behaviour, it seems that selecting the cell through cell.setSelected() it's not the same as invoking tableView.selectRowAtIndexPath() method. Selecting the row with the latest does the job perfectly and solves the issue.

Upvotes: 2

Nitin Gohel
Nitin Gohel

Reputation: 49710

Create an array like

var arrSelectCell = [NSIndexPath]()

And do the code at didSelectRowAtIndexPath like following:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if arrSelectCell.contains(indexPath) {
        if let oldCell = tableView.cellForRowAtIndexPath(indexPath) {

                if let index = arrSelectCell.indexOf(indexPath) {
                    arrSelectCell.removeAtIndex(index)
                }
            tableView.deselectRowAtIndexPath(indexPath, animated: true)
            oldCell.accessoryType = .None
        }

    }
    else
    {
        arrSelectCell.append(indexPath)
        if let selectCell = tableView.cellForRowAtIndexPath(indexPath) {
            tableView.deselectRowAtIndexPath(indexPath, animated: true)
            selectCell.accessoryType = .Checkmark
        }


    }    

and also dont forget to set a code in cellForRowAtIndexPath to check already checked and unchecked cell maintain after reuse cell also. So need to few code you need to write as following.

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

            let cell = tableView.dequeueReusableCellWithIdentifier("serchCell", forIndexPath: indexPath) as! SearchTableViewCell

            if arrSelectCell.contains(indexPath)
            {
                    cell.accessoryType = .Checkmark
            }
            else
            {
                    cell.accessoryType = .None
            }

            return cell
        }

Upvotes: 0

Related Questions