Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS UITableViewCell setSelected:animated: always has animated = NO

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.

like image 821
NKorotkov Avatar asked Jun 09 '15 14:06

NKorotkov


1 Answers

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:

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated]

That will happen for you automatically if you are using a UITableViewController and have its clearsSelectionOnViewWillAppear property to YES.

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.

like image 50
Dave Batton Avatar answered Oct 21 '22 11:10

Dave Batton