Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView with preview and paging enabled

I am trying to imitate what Apple has when showing the search result in the App Store. (reference: http://searchengineland.com/apple-app-search-shows-only-one-result-at-a-time-133818)

It shows like the detailed-application-info in a cards and it is paged. I am stuck at how to make the previous-and-next card shows when one active card in the middle and the scroll view's paging behaviour is still intact.

I have tried using the UICollectionView and set the clipSubviews to NO, hoping that it will show the previous page and the next page, but as soon as the cell goes off-screen, the cell gets hidden (removed from the view hierarchy) and not displayed. I think thats the flyweight pattern of the UICollectionView (the behavior of UICollectionView). Any ideas of what would be possible?

Cheers,

Rendy Pranata

like image 818
Rpranata Avatar asked Feb 07 '13 06:02

Rpranata


1 Answers

The problem: UICollectionView as a subclass of UIScrollView essentially animates its bounds by a stride of bounds.size. Although this could mean that all you had to do is decrease the bounds while keeping the frame bigger, unfortunately UICollectionView will not render any cells outside its current bounds... destroying your preview effect.

The Solution:

  1. Create a UICollectionView with paging set to NO and with the desired frame.
  2. Create UICollectionViewCells that are smaller than the UICollectionView's frame/bounds. At this stage, a part of the next cell should show in the frame. This should be visible before implementing the other steps below.
  3. Add a collectionView.contentInset.left and right (I assume your layout is horizontal) equal to the contentOffsetValue method (as shown below for simplicity) so as to align the first and last cells to the middle.
  4. Create a UICollectionViewFlowLayout which overrides the method that gives the stopping point like so:

Like so:

-(CGFloat)contentOffsetValue
{
    return self.collectionView.bounds.size.width * 0.5f - self.itemSize.width * 0.5f;
}

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{

    static float EscapeVelocity = 0.5f; // otherwise snap back to the middle
    NSArray* layoutAttributesArray = [self layoutAttributesForElementsInRect:self.collectionView.bounds];

    if(layoutAttributesArray.count == 0)
        return proposedContentOffset;

    CGFloat currentBoundsCenterX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width * 0.5f;


    UICollectionViewLayoutAttributes* candidateNextLayoutAttributes = layoutAttributesArray.firstObject;


    for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray)
    {
        if ((layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) ||
            (layoutAttributes == candidateNextLayoutAttributes)) // skip the first comparison
            continue;

        if(velocity.x > EscapeVelocity || velocity.x < -(EscapeVelocity))
        {
            if(velocity.x > EscapeVelocity && layoutAttributes.center.x > candidateNextLayoutAttributes.center.x)
            {

                candidateNextLayoutAttributes = layoutAttributes;
            }

            else if (velocity.x < -(EscapeVelocity) && layoutAttributes.center.x < candidateNextLayoutAttributes.center.x)
            {

                candidateNextLayoutAttributes = layoutAttributes;
            }
        }
        else
        {
            if(fabsf(currentBoundsCenterX - layoutAttributes.center.x) < fabsf(currentBoundsCenterX - candidateNextLayoutAttributes.center.x))
            {

                candidateNextLayoutAttributes = layoutAttributes;
            }
        }   
    }
    return CGPointMake(candidateNextLayoutAttributes.center.x - self.collectionView.bounds.size.width * 0.5f, proposedContentOffset.y);

}
like image 58
Mike M Avatar answered Sep 30 '22 11:09

Mike M