I'm trying to make my own selection animation. I've created a subclass of UITableViewCell. I do my selection animation in -setSelected:animated:
method. It works as intended when you select or deselect cells by tapping them. Problem is that animation is also seen during scrolling, since -setSelected:animated:
is called on each cell before it appears. This is how reusing cells mechanism works, I get it. What I don't get is that it always calls this method with animated = NO either on tap or on scroll. This seems like a logic mistake to me. I presumed it was supposed to select cells with animation when you tap them and without animation when reused cell appears. Is animated
parameter even ever used anywhere except manual calls? Here's my code:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
BOOL alreadySelected = (self.isSelected) && (selected);
BOOL alreadyDeselected = (!self.isSelected) && (!selected);
[super setSelected:selected animated:animated];
if ((alreadySelected) || (alreadyDeselected)) return;
NSLog(@"Animated selection: %@", animated ? @"YES" : @"NO");
NSTimeInterval duration;
if (animated) {
duration = 0.25;
} else {
duration = 0.0;
}
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
if (selected) {
//layer properties are changed here...
} else {
//layer properties are changed here...
}
[CATransaction commit];
}
This always goes without the animation.
I can't think of any other such an easy way to handle custom selection. Implementing -didSelectRow
methods in controller seems so much worse and it's not called during scrolling, so reused cells will appear in a wrong state. Any idea how to fix this?
UPDATE:
I've found a temporary solution:
#pragma mark - Table View Delegate
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setSelected:YES animated:YES];
return indexPath;
}
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setSelected:NO animated:YES];
return indexPath;
}
It does work, but I don't like it. The fact that TableView's delegate has to know something about selection and it's not all contained in one place bugs me a lot. And -setSelected
is called twice when taping a row - with and without animation.
This is how table views were designed to work. When you select it it highlights immediately, so no animation is needed. However, you will (you should, anyway) see the table view cell deselect with animation when you pop back to the table view. You can see that by using this code in the
-viewWillAppear:override:
That will happen for you automatically if you are using a
UITableViewController
and have itsclearsSelectionOnViewWillAppear
property toYES
.If you want a different behavior, you need to code it yourself. If the code you posted here is working to your liking, keep it. You could also modify the cell subclass to always pass
YES
to the superclass in the-setSelected:animated:
method override.