phi
phi

Reputation: 10733

Why is -didDeselectRowAtIndexPath not being called?

I created a fresh project (Xcode 4, Master-Detail application) just to see if I'm doing something wrong, but I still have the same problem. I want to call -reloadData when the user deselects a cell, so this is my code:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [tableView reloadData];
}

-(NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    return indexPath;
}

The problem is that didDeselectRowAtIndexPath and willDeselectRowAtIndexPath don't seem to be called. Is this the expected behavior? The docs for tableView:didDeselectRowAtIndexPath: state

Tells the delegate that the specified row is now deselected.

so I guess that it should work as I thought.

Upvotes: 28

Views: 33604

Answers (12)

Frank Schmitt
Frank Schmitt

Reputation: 25785

One source of this issue is setting isSelected directly on the table view cell, rather than telling the table view to select the cell via selectRow(at:animated:scrollPosition:) or deselect it via deselectRow(at:animated:).

The cell's isSelected property is not the source of truth that the table view uses to determine whether to call the delegate's didDeselect method (although a cell whose isSelected is set does seem to prevent the table view from calling the delegate's didSelect method—meaning that the cell can get in a state where it is impossible to select or deselect via the UI).

Upvotes: 0

Ashu
Ashu

Reputation: 3521

First of all, you have to set allowsMultipleSelection is true and set delegate by below code

self.tableView.allowsMultipleSelection = true
self.tableView.delegate = self

If you use tableView.reloadData() in didSelectRowAt delegate method, remove this.

in your custom cell, use selected method.

override func setSelected(_ selected: Bool, animated: Bool) {
   super.setSelected(selected, animated: animated)
   //write your code here for selection method
}

By this method, your cell state is selected. If you again click on selected cell, then didDeSelect delegate method will call automatically.

Upvotes: 0

Jack
Jack

Reputation: 14379

For me it's started working by adding ->super.setSelected(selected, animated: animated)

override func setSelected(_ selected: Bool, animated: Bool) {
   super.setSelected(selected, animated: animated)//This was missing
}

Upvotes: 1

Abdelrahman Fouda
Abdelrahman Fouda

Reputation: 21

You just have to set the selection to be "Multiple Selection" as Xcode allow you to deselect the cells in this mode only.

Xcode Screenshot

Upvotes: 2

Zsivics Sanel
Zsivics Sanel

Reputation: 1067

When any cell is selected the first time, the -[UITableViewDelegate tableView:didDeselectRowAtIndexPath:] method is not called, but the -[UITableViewDelegate tableView:didSelectRowAtIndexPath:]. Just after selecting one more cell, the didDeselectRowAtIndexPath is called right after the didSelectRowAtIndexPath.

This is OK.

But if you have to show a cell as selected at the begining, (e.q. using UITableViewCellAccessoryCheckmark), then, after selecting another cell you probably want the didDeselectRowAtIndexPath method being called the first time, to deselect the previous cell.

The solution!

You have to call the -[UITableView selectRowAtIndexPath:animated:scrollPosition:] in the -[UITableViewDataSource tableView:cellForRowAtIndexPath:] to notify that a wanted cell is already selected.

Objective-C

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    // saving the current selected row
    SelectedRow = indexPath.row;
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;
}

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }

    // preventing selection style
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    cell.textLabel.text = "Some text";

    if (indexPath.row == SelectedRow) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        // just wanted to make it selected, but it also can scroll to the selected position
        [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    }

    return cell;
}

Swift 3.1

// MARK: - UITableViewDelegate

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = UITableViewCellAccessoryType.checkmark
    selectedRow = indexPath.row
}

override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = UITableViewCellAccessoryType.none
}

// MARK: - UITableViewDataSource

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")

    // preventing selection style
    cell.selectionStyle = UITableViewCellSelectionStyle.none

    cell.textLabel?.text = "some text"

    if (indexPath.row == selectedRow) {
        cell.accessoryType = UITableViewCellAccessoryType.checkmark
        // just wanted to make it selected, but it also can scroll to the selected position
        tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)
    }

    return cell
}

Upvotes: 4

kevmalek
kevmalek

Reputation: 1353

A clean way of solving this problem is to do the following:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
    // Do your cell setup work here...

    //If your cell should be selected...
    if cellShouldBeSelected {
        tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
    }

    return cell
}

This solves this entire problem of a cell not responding to being deselected after a tableView.reloadData()call happens.

Upvotes: 6

TheBritishAreComing
TheBritishAreComing

Reputation: 1727

I know this is an old question but I just ran into the same problem.

If you use

[tableView reloadData]

Then The table data is reloaded and no rows are selected behind the scenes - meaning

only

didSelectRowAtIndexPath

is ever called. I hope this helps someone who comes across this problem.

Upvotes: 10

Randika Vishman
Randika Vishman

Reputation: 8134

I think it's just simple mistake! Why don't you use following:

[self.tableView deselectRowAtIndexPath:indexPath animated:YES];

Instead of using just "tableView" in that line?
I guess, and pretty sure that above line would give you the solution!
hope this helped you, if you looked back at your old question!!!
Kudos! :)

Upvotes: 0

Kannan Prasad
Kannan Prasad

Reputation: 1806

Set allowsMultipleSelection for that tableview to TRUE

  self.tableView.allowsMultipleSelection = YES;

Upvotes: 1

Andrew
Andrew

Reputation: 12009

One other quirk that I've found — in IOS 5.1, at any rate — is that if you call reloadData on the table, you won't get didDeselectRowAtIndexPath for any selected rows. In my case, I adjust the cell layout slightly depending on whether it's selected or not, so I needed to manually do that work prior to reloading the table data.

Upvotes: 12

freytag
freytag

Reputation: 4819

If you call deselectRowAtIndexPath:animated:, the delegate methods tableView:willDeselectRowAtIndexPath: and tableView:didDeselectRowAtIndexPath: message are not sent.

Upvotes: 55

beryllium
beryllium

Reputation: 29767

the documentation of tableView:willDeselectRowAtIndexPath: also says that

This method is only called if there is an existing selection when the user tries to select a different row. The delegate is sent this method for the previously selected row. You can use UITableViewCellSelectionStyleNone to disable the appearance of the cell highlight on touch-down.

It not worked for we when I used UITableViewCellSelectionStyleNone.

Upvotes: 25

Related Questions