Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to snap collection view items to one per swipe regardless of scrolling velocity

I'm using the code from the Apple demo in a subclass of UICollectionViewFlowLayout:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    CGFloat offsetAdjustment = MAXFLOAT;
    CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
    CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);

    UICollectionViewLayoutAttributes *targetAttributes = nil;
    NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
    for (UICollectionViewLayoutAttributes *a in attributes) {
        CGFloat itemHorizontalCenter = a.center.x;
        if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
            offsetAdjustment = itemHorizontalCenter - horizontalCenter;
            targetAttributes = a;
        }
    }

    return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

This works in the sense that whenever I swipe / pan and release, an item will snap into place. But the proposed content offset is proportional to the swipe velocity. What I'm trying to do is that, no matter how fast / slow I swipe, the collection view only snaps to the next item immediately before or after the one that's currently centered.

I tried doing this:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    NSInteger currentPage = (int)(self.collectionView.contentOffset.x / self.collectionView.bounds.size.width);
    if (proposedContentOffset.x > self.collectionView.contentOffset.x) {
        currentPage = (MIN(currentPage + 1, ((int)(self.collectionView.contentSize.width / self.collectionView.bounds.size.width)) - 1));
    }

    proposedContentOffset.x = self.collectionView.bounds.size.width * currentPage;

    CGFloat offsetAdjustment = MAXFLOAT;
    CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
    CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);

    UICollectionViewLayoutAttributes *targetAttributes = nil;
    NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
    for (UICollectionViewLayoutAttributes *a in attributes) {
        CGFloat itemHorizontalCenter = a.center.x;
        if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
            offsetAdjustment = itemHorizontalCenter - horizontalCenter;
            targetAttributes = a;
        }
    }

    return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

But it's not quite right. Sometimes it'll skip an item and snap to the next one after that (that is, it'll not snap to the item immediately next to the one I start scrolling from, but the one next to the one immediately next).

Any thoughts?

like image 318
SaldaVonSchwartz Avatar asked Dec 05 '13 22:12

SaldaVonSchwartz


1 Answers

Ok so I figured how to make it work by making everything relative to the current contentOffset:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    if (proposedContentOffset.x > self.collectionView.contentOffset.x) {
        proposedContentOffset.x = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width / 2.;
    }
    else if (proposedContentOffset.x < self.collectionView.contentOffset.x) {
        proposedContentOffset.x = self.collectionView.contentOffset.x - self.collectionView.bounds.size.width / 2.;
    }

    CGFloat offsetAdjustment = MAXFLOAT;
    CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
    CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);

    NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
    for (UICollectionViewLayoutAttributes *a in attributes) {
        CGFloat itemHorizontalCenter = a.center.x;
        if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
            offsetAdjustment = itemHorizontalCenter - horizontalCenter;
        }
    }

    return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
like image 130
SaldaVonSchwartz Avatar answered Nov 03 '22 00:11

SaldaVonSchwartz