2
votes

I have a UICollectionView. I want a behaviour where when the user touches a cell, it scales down, as if it is getting pushed down slightly. I've accomplished this by using the UICollectionViewDelegate methods:

- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [self scaleDownCell:cell];
}

- (void) collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    [self scaleUpCell:cell];
}

My problem is that sometimes my cell will start to scale down, and then suddenly go back to full size, without performing the scale up animation, creating jerky effect. I have checked with breakpoints that it scales up before my scale up method is called. I am trying to figure out why.

The scale functions are as follows:

+ (void)scaleDownCell:(UICollectionViewCell *)cell
{
    CGAffineTransform transform = CGAffineTransformMakeScale(0.95, 0.95);    
    [UIView animateWithDuration:SCALE_DOWN_ANIMATION_DURATION
                          delay:0.0
         usingSpringWithDamping:SCALE_DOWN_SPRING_DAMPING
          initialSpringVelocity:SCALE_DOWN_SPRING_VELOCITY
                        options:0
                     animations:^{
                         cell.transform = transform;
                     }
                     completion:^(BOOL finished) {
                     }];
}

+ (void)scaleUpCell:(UBCollectionViewCell *)cell
{
    CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);    
    [UIView animateWithDuration:SCALE_UP_ANIMATION_DURATION
                          delay:0.3
         usingSpringWithDamping:SCALE_UP_SPRING_DAMPING
          initialSpringVelocity:SCALE_UP_SPRING_VELOCITY
                        options:0
                     animations:^{
                         cell.transform = transform;
                     }
                     completion:^(BOOL finished) {
                     }];
}

EDIT: I basically want to replace highlighting in my cells with this scaling behaviour, it serves the exact same purpose and I want it to happen in exactly the same situation that the cell would be highlighted, so it seemed like an appropriate place to apply my transformation.

Regardless, I had first tried overriding the UIResponder touch methods in the cell subclass, but was observing the same behaviour. The sudden scale up happens even if I delete the scale up method so that it is never called, so my scale down is not being cancelled by the scale up animation. I can see through log statements that my scale down is called once, and the animation completion block is getting called with finsished == YES. The cell is getting scaled back up BEFORE the completion block is called (I have set the animation duration to be extra long to help in debugging). This seems to happen when I touch on the cell and then quickly scroll, so I guess the scrolling has something to do with it.

1
Breakpoints won't reveal whether the transform has finished animating, as this is performed on the layer's property and immediately set as such once the animation begins. Can you post your defined constants -- are you sure that the scale up delay (0.3) is greater than the SCALE_DOWN_ANIMATION_DURATION constant?bdev
Thanks for your comment. I have played around with different constants to try to figure out what was happening, but as is stated in my edited question, even if I never call the scale up method I still see the same reset of my transform.Darren
@Darren then something else is going on. After all, all the scale up method does is set the transform to a scale of 1 - which is to say, no effect. So if you are seeing the thing scale up, then clearly you have other code, unknown to yourself and not quoted above, that is doing that! You need to find that code.matt
Either that or I wonder if this is the classic problem where auto layout and view transforms are enemies (which I discuss at length here: stackoverflow.com/questions/12943107/…) - try replacing your UIView animation with a CABasicAnimation and see if that fixes it.matt
I have another thought - your comment about scrolling is revealing. Think how a collection view layout works - it is responsible for the attributes of each cell, including its transform (developer.apple.com/library/ios/documentation/uikit/reference/…). So naturally if you change the transform of the cell yourself, and you don't tell the layout, and the layout takes charge, it will cancel your transform.matt

1 Answers

1
votes

The problem is that you are hijacking the notion of "highlighting" for something that it isn't. Highlighting is a complicated and confusing business. In the course of being tapped and selected, the cell highlights and unhighlights more than once. Thus it is the wrong thing to respond to.

If what you are trying to detect is a touch, then you should respond to touch. If a gesture recognizer won't do, then use a cell subclass of your own so that you can implement UITouch detection directly.

One more thing to keep in mind is that, the way you've written this, if the scale up happens while the scale down is in progress, it will just kill it dead.

EDIT - One final thought - in your edit, you make a comment about scrolling having something to do with this. That makes sense. Think how a collection view layout works - it is responsible for the attributes of each cell, including its transform (https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewLayoutAttributes_class/Reference/Reference.html). So naturally if you change the transform of the cell yourself, and you don't tell the layout, and the layout takes charge, it will cancel your transform.