Question

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?

Was it helpful?

Solution 2

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];
        }
    }];
}

OTHER TIPS

Here's what I ended up with for my UICollectionView (horizontal scrolling like the UIPickerView):

@implementation UIInfiniteCollectionView

- (void) recenterIfNecessary {
    CGPoint currentOffset = [self contentOffset];
    CGFloat contentWidth = [self contentSize].width;
    // don't just snap to center, since this might be done in the middle of a drag and not aligned.  Make sure we account for that offset
    CGFloat offset = kCenterOffset - currentOffset.x;
    int delta = -round(offset / kCellSize);
    CGFloat shift = (offset + delta * kCellSize);
    offset += shift;
    CGFloat distanceFromCenter = fabs(offset);

    // don't always recenter, just if we get too far from the center.  Eliza recommends a quarter of the content width
    if (distanceFromCenter > (contentWidth / 4.0)) {
        self.contentOffset = CGPointMake(kCenterOffset, currentOffset.y);
        // move subviews back to make it appear to stay still
        for (UIView *subview in self.subviews) {
            CGPoint center = subview.center;
            center.x += offset;
            subview.center = center;
        }
        // add the offset to the index (unless offset is 0, in which case we'll assume this is the first launch and not a mid-scroll)
        if (currentOffset.x > 0) {
            int delta = -round(offset / kCellSize);
            // MODEL UPDATE GOES HERE
        }
    }
}

- (void) layoutSubviews { // called at every frame of scrolling
    [super layoutSubviews];
    [self recenterIfNecessary];
}

@end

Hope this helps someone.

I have not used UICollectionView for infinite scrolling, but when doing it with a UIScrollView you first adjust your content offset (instead of using scrollRectToVisible) to the location you want. Then, you loop through each subview in your scroller and adjust their coordinates either to the right or left based on the direction the user was scrolling. Finally, if either end is beyond the bounds you want them to be, move them to the far other end. Their is a very good WWDC video from apple about how to do infinite scrolling you can find here: http://developer.apple.com/videos/wwdc/2012/

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