简体   繁体   中英

UITableViewCell selector setSelected:animated: gets called many times?

I've discovered a strange behavior with setSelected:animated: in my custom UITableViewCell class. I discovered that this function gets called multiple times if I click on a cell in my table. I am wondering if this is normal behavior or a bug in my code.

To help with debugging, I've modified the setSelected:animated: function in my custom UITableViewCell class implementation as such:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {

[super setSelected:selected animated:animated];

// Configure the view for the selected state.
if (selected)
    NSLog(@"Yes %X", &self);
else
    NSLog(@"No %X", &self);

}

If I click on a cell in the simulator, here is what I get in the console:

2011-03-22 22:05:26.963 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:05:26.964 marketPulse[3294:207] Yes BFFFDE30

You would think that I would get only 1 entry, since I only clicked on 1 cell.

And if I click on a different cell after that:

2011-03-22 22:07:11.014 marketPulse[3294:207] No BFFFD890
2011-03-22 22:07:11.016 marketPulse[3294:207] No BFFFDD00
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDE30

If I click on the same cell 2 times in a row, I get more than 2 Yes :

2011-03-22 22:08:41.067 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:08:41.068 marketPulse[3294:207] Yes BFFFDE30
2011-03-22 22:08:41.069 marketPulse[3294:207] Yes BFFFDE30

The more times I click the same cell, the more Yes I will get, and if I click on a different cell after that, I'll get a lot of No

I put a breakpoint before the NSLog, and looking at the debugger, it seems that all the repeated calls are coming from the same object.

Here is a part of my tableView:cellForRowAtIndexPath: function so you can see how my cells are being treated:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


static NSString *ContentCellIdentifier = @"newsTableCellContent";

UITableViewCell *cell;


//index of cell data in tableData
NSUInteger index = indexPath.row / 2;

...

//content of story
else if( [indexPath row] % 2 == 1 ) {

    cell = [tableView dequeueReusableCellWithIdentifier:ContentCellIdentifier];

    if (cell == nil) {
        NSArray *topLevelObjects = [[NSBundle mainBundle]
                                    loadNibNamed:@"newsTableCells"
                                    owner:nil options:nil];

        for (id currentObject in topLevelObjects) {
            if ( [currentObject isKindOfClass:[newsTableCellContent class]] ) {
                cell = currentObject;
                break;
            }
        }
    }

    ((newsTableCellContent *)cell).content.text = [[tableData objectAtIndex:index] description];

}   

return cell;
}

Everything works fine so its hard to tell if the repeat calls to setSelected:animated: are intentional or not. If this is normal operation, I can make do with another method, but I would just like to know if this is suppose to happen or not.

Thanks

What's going on is simply that the UITableView keeps track of which cells are selected in the table.

Since cells are reused when you scroll through a large table view, the table view has to keep the list of selected cells separate. Not only that, but whenever it reuses a cell it has to set its selected property, because it may be using an old, invalid selected state from a previous incarnation.

When you tap a cell, several things happen: the previously selected cell is deselected (using setSelected: ). The new cell is highlighted. It's de-highlighted (at least if you tap, instead of holding your finger down), and the setSelected: method is called because the new cell was selected. That's one.

The second call is a delayed perform call, possibly from a point where the table view didn't yet know what the final state of the table would be. This call goes to _selectAllSelectedRows , which, as the name suggests, calls 'setSelected:animated:' on all selected rows. That's the second call. The reason for this is most likely to address potential issues due to the the table view being in a "transition", but who knows.

Whether it's a bug or not is up for interpretation. A fix for the duplicate calls is to simply do:

if (self.selected == selected) return;

right before the call to super (you do not have to call super if self.selected == selected ).

Ideally you should not be calling setSelected from anywhere in your code. UIKit will take care of calling it.

If you want to show a cell/row as selected in cellForRowAtIndexPath method simply call

tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: .None)

for that specific indexPath.

Again never ever call setSelected explicitly unless you really mean to.

This is a normal behavior if you're using iPad. (it is only called once on iPhone).

In order to stop getting multiple "setSelected:YES" or multiple "setSelected:NO", all you have to do is this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

Now, 1 click on any cell gives you:

  • 1 entry of setSelected:YES animated:NO
  • 1 entry of tableView: didSelectRowAtIndexPath:
  • 1 entry of setSelected:NO animated:YES

So, calls are now stable regardless of what you do.

It should definitely be called when table is scrolled. Cells are reused, that means, if you scroll cells in invisible areas will be reused and reinitialized, including the call to setSelected, which is basically a lightweight property setter.

If you really want to see what's happening, add a NSLog to tableView:cellForRowAtIndexPath: which will log indexPath and the returned cell.

The entire log should give you a good understanding what happens inside and why. I suppose it will be something like this (Clicked on IndexPath 1:1)

Give me cell on 1:0 (previously selected cell).
Deselect 1:0
Give me cell on 1:0 again (updated after deselection)
Deselect 1:0 (update selected flag on this cell and trigger animation)
Give me cell on 1:1
Select 1:1
Give me cell on 1:1 again (updated after selection)
Select 1:1 (update selected flag on this cell and trigger animation)

Clicking on a selected cell again is only slightly different - instead of triggering unselecting, it triggers another update.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM