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
).
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:
So, calls are now stable regardless of what you do.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With