Search code examples
iphoneiosipadcaanimationaqgridview

AQGridView cells get swapped around when CAAnimation is applied


Has anyone had success applying animations to AQGridViewCells? I'm trying to make every cell but the first one fade away after a tap.

The problem is that when the fade begins, the cell contents usually get swapped around. For example if the first row of the grid view has cells with labels "1", "2", "3", then the labels might get swapped to "1", "3", "2".

- (AQGridViewCell *)gridView:(AQGridView *)aGridView cellForItemAtIndex:(NSUInteger)index
{
    static NSString *CellIdentifier = @"ReusableGridViewCell";

    AQGridViewCell *cell = (AQGridViewCell *)[gridView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        [[NSBundle mainBundle] loadNibNamed:@"ReusableGridViewCell" owner:self options:nil];

        cell = [[[AQGridViewCell alloc] initWithFrame:gridViewCellContent.frame
                                      reuseIdentifier:CellIdentifier] autorelease];
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        [cell.contentView addSubview:label];
        [label release];

        cell.selectionStyle = AQGridViewCellSelectionStyleNone;
    }

    UILabel *label = [[cell.contentView subviews] objectAtIndex:0];
    if (! tapped)
    {
        label.text = [NSString stringWithFormat:@"%u", index];
    }   
    else if (index > 0)
    {
        CATransition *cellAnimation = [CATransition animation];
        cellAnimation.duration = 3.0;
        cellAnimation.type = kCATransitionFade;
        cellAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
        [label.layer addAnimation:cellAnimation forKey:kCATransition];              // labels get swapped
//      [cell.contentView.layer addAnimation:cellAnimation forKey:kCATransition];   // labels get swapped
//      [cell.layer addAnimation:cellAnimation forKey:kCATransition];               // labels get swapped, one cell immediately disappears
        NSLog(@"%u", [gridView isAnimatingUpdates]);                                // prints "0"

        label.hidden = YES;
    }

    return cell;
}

- (void)gridView:(AQGridView *)aGridView didSelectItemAtIndex:(NSUInteger)index
{
    tapped = YES;
    [gridView reloadData];
}

I tried setting breakpoints in a bunch of AQGridView, AQGridViewCell, etc. methods to try to find the one that causes the swap. Couldn't find it.

In the known bugs of AQGridView, there is this:

Don’t try to pile multiple animations on top of one another. i.e. don’t call -beginUpdates on a grid view whose -isAnimatingUpdates method returns YES. Bad things will happen, cells will end up in the wrong places, stacked on top of one another.

In the above code, -isAnimatingUpdates returns NO. Even so, perhaps what I'm seeing is another related bug in AQGridView -- I'll be submitting a bug report. But since mine is such a simple case I'm wondering if somebody has encountered it before and figured out a workaround, perhaps some way to turn off the animations inside of AQGridView.

Edit

To see if the problem was related to the hidden property, I instead animated the cell's opacity (tried both approaches described here). Even when opacity only goes down to 0.5 instead of 0.0, the cells are still swapping.


Solution

  • Here's a workaround. If anyone finds a more elegant solution I will mark it as the answer.

    - (void)gridView:(AQGridView *)aGridView didSelectItemAtIndex:(NSUInteger)index
    {
        // Create a copy of the 0th cell's content and display it on top of that cell.
        AQGridViewCell *selectedCell = [aGridView cellForItemAtIndex:0];
        UILabel *selectedLabel = [[selectedCell.contentView subviews] objectAtIndex:0];
        CGRect frame = [self.view convertRect:selectedLabel.frame fromView:selectedLabel.superview];
        UILabel *labelOnTop = [[UILabel alloc] initWithFrame:frame];
        labelOnTop.text = selectedLabel.text;
        [self.view addSubview:labelOnTop];
        [labelOnTop release];
    
        // Fade away the grid view as a whole (not individual cells). 
        CATransition *cellAnimation = [CATransition animation];
        cellAnimation.duration = 3.0;
        cellAnimation.type = kCATransitionFade;
        cellAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
        [aGridView.layer addAnimation:cellAnimation forKey:kCATransition];
        aGridView.hidden = YES;
    }