infinite-scrolluicollectionviewscroll-paging

How to make an UICollectionView with infinite paging?


I have a UICollectionView with 6 pages, and paging enabled, and a UIPageControl. What I want is, when I came to the last page, if I drag to right, UICollectionView reloads from first page seamlessly.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{

// The key is repositioning without animation
if (collectionView.contentOffset.x == 0) {
    // user is scrolling to the left from image 1 to image 10.
    // reposition offset to show image 10 that is on the right in the scroll view
    [collectionView scrollRectToVisible:CGRectMake(collectionView.frame.size.width*(pageControl.currentPage-1),0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];
}
else if (collectionView.contentOffset.x == 1600) {
    // user is scrolling to the right from image 10 to image 1.
    // reposition offset to show image 1 that is on the left in the scroll view
    [collectionView scrollRectToVisible:CGRectMake(0,0,collectionView.frame.size.width,collectionView.frame.size.height) animated:NO];

}
pageControlUsed = NO;

}

It doesn't work like I want. What can I do?


Solution

  • I've been using the Street Scroller sample to create an infinite scroller for images. That works fine until I wanted to set pagingEnabled = YES; Tried tweaking around the recenterIfNecessary code and finally realized that it's the contentOffset.x that has to match the frame of the subview that i want visible when paging stops. This really isn't going to work in recenterIfNecessary since you have no way of knowing it will get called from layoutSubviews. If you do get it adjusted right, the subview may pop out from under your finger. I do the adjustment in scrollViewDidEndDecelerating. So far I haven't had problems with scrolling fast. It will work and simulate paging even when pagingEnabled is NO, but it looks more natural with YES.

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
        [super scrollViewDidEndDecelerating:scrollView];
    
        CGPoint currentOffset = [self contentOffset];
    
        // find the subview that is the closest to the currentOffset.
        CGFloat closestOriginX = 999999;
        UIView *closestView = nil;
        for (UIView *v in self.visibleImageViews) {
            CGPoint origin = [self.imageContainerView convertPoint:v.frame.origin toView:self];
            CGFloat distanceToCurrentOffset = fabs(currentOffset.x - origin.x);
            if (distanceToCurrentOffset <= closestOriginX) {
                closestView = v;
                closestOriginX = distanceToCurrentOffset;
            }
        }
    
        // found the closest view, now find the correct offset
        CGPoint origin = [self.imageContainerView convertPoint:closestView.frame.origin toView:self];
        CGPoint center = [self.imageContainerView convertPoint:closestView.center toView:self];
        CGFloat offsetX = currentOffset.x - origin.x;
    
        // adjust the centers of the subviews
        [UIView animateWithDuration:0.1 animations:^{
            for (UIView *v in self.visibleImageViews) {
                v.center = [self convertPoint:CGPointMake(v.center.x+offsetX, center.y) toView:self.imageContainerView];
            }
        }];
    }