Question

I would like to rearrange UICollectionView cells during the rotation of my device in a similar way this is done in the evernote iPads app for the notes. In the default implementation there is just a fade-in and fade-out of the cells but i would like to have the cells to move around during the rotation.

What would be the recommended way to achieve a similar animation? Do I need to create a custom UICollectionViewLayout?

Was it helpful?

Solution

I managed to get the desired rotation effect by subclassing UICollectionViewFlowLayout and overriding the two methods: initialLayoutAttributesForAppearingItemAtIndexPath and finalLayoutAttributesForDisappearingItemAtIndexPath which are the two points of controls to respectively define the starting/final layout information for an item being inserted into the collection view.

See source code:

.h:

#import "UICollectionView.h"

@interface UITestCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

.m:

#import "UITestCollectionViewFlowLayout.h"

@interface UITestCollectionViewFlowLayout () 
{
    BOOL _isRotating; 
}

@property (strong, nonatomic) NSIndexPath* lastDissappearingItemIndex;

@end

@implementation UITestCollectionViewFlowLayout

@synthesize lastDissappearingItemIndex = _lastDissappearingItemIndex;

// returns the starting layout information for an item being inserted into the collection view
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{

    UICollectionViewLayoutAttributes* attributes = (UICollectionViewLayoutAttributes *)[self layoutAttributesForItemAtIndexPath:itemIndexPath];

    if (_isRotating) // we want to customize the cells layout only during the rotation event
    {
        if ([self.lastDissappearingItemIndex isEqual:itemIndexPath])
            return nil; // do not animate appearing cell for the one that just dissapear
        else
        {
            attributes.alpha = 0;

            // setting the alpha to the new cells that didn't match the ones dissapearing is not enough to not see them so we offset them
            attributes.center =  CGPointMake(attributes.center.x, attributes.size.height * 2 + attributes.center.y);
        }

    }

    return attributes;
}

// returns the final layout information for an item that is about to be removed from the collection view
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath*)itemIndexPath
{

    UICollectionViewLayoutAttributes* attributes = (UICollectionViewLayoutAttributes *)[self layoutAttributesForItemAtIndexPath:itemIndexPath];

    if (_isRotating)
    {
        attributes.alpha = 1.0;

        self.lastDissappearingItemIndex = itemIndexPath;
    }

    return attributes;
}

- (void) viewController:(UIViewController *)viewController didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    _isRotating = NO;
}

- (void) viewController:(UIViewController *)viewController willRotateToInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration
{
    _isRotating = YES;
}

Note that i am using a flag that i set from the viewcontroller who create this flowlayout to know when we are actually rotating or not; the reason being that i want this effect to happen only during rotation.

I am interested to hear feedbacks/improvement about this code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top