2

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.

Darren
  • 10,091
  • 18
  • 65
  • 108
  • 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 Aug 01 '14 at 21:40
  • 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 Aug 03 '14 at 21:38
  • @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 Aug 03 '14 at 21:52
  • 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: http://stackoverflow.com/questions/12943107/how-do-i-adjust-the-anchor-point-of-a-calayer-when-auto-layout-is-being-used/14105757#14105757) - try replacing your UIView animation with a CABasicAnimation and see if that fixes it. – matt Aug 03 '14 at 21:54
  • 1
    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_ (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. – matt Aug 03 '14 at 21:56
  • YES!! I haven't completely figured it out yet but it does seem to have something to do with the UICollectionViewLayout that is being used. I'll post more when I figure out more. – Darren Aug 04 '14 at 03:02

1 Answers1

1

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for your answer, I added my comments addressing it in my question as they were too long for a comment. – Darren Aug 03 '14 at 21:36
  • 1
    The transform was getting reset by the UICollectionViewLayoutAttributes returned by the layoutAttributesForElementsInRect: method in the UICollectionViewLayout subclass. I was able to fix this by setting the attributes transform property to the transform of the corresponding collection view cell in this method. Thanks for the help! – Darren Aug 06 '14 at 03:34
  • Glad you got it fixed! – matt Aug 06 '14 at 04:58