4

I'm trying to implement infinite horizontal and vertical scrolling from within a UICollectionView in iOS 6. I have managed to get it working, but it's slow and choppy.

There are similar questions asked on StackOverflow but the deepest any of them go is follow Apple's WDC video and implement with collection instead of scroll. I based my design off of this and other comments made in similar questions but am still unable to get it to look smooth.

I am basically trying to mimic the behavior of the HBO GO iPad app, which presents a grid of shows and allows you to scroll them in any direction, when you reach the end it just wraps the shows around so you feel like you are infinitely scrolling.

What I've done is create a custom Layout that arranges UICollectionView cells in a grid. Then I subclassed a UICollectionView overwrote it's layoutSubviews method and created a reecenterIfNecessary function just like Eliza's WDC video shows. This works, and I get infinite "scrolling" but the content doesn't change.

So I created a delegate that pointed to the view controller and whenever it wraps, I tell the delegate to move it's data source, and it does, but it is not reflected in the UICollectionView, so one final call to [self reloadData] refreshes the view and everything "works". I get infinite scrolling.

But because I call reloadData, the delay is noticeable and the scrollbars flicker around (from the "content" being "reloaded"). This is unacceptable and I can't figure out how to get around it!

Any help is appreciated. I included the recenterIfNecessary function in case it helps.

- (void) recenterIfNecessary{

CGPoint currentOffset = [self contentOffset];
CGFloat contentHeight = [self contentSize].height;
CGFloat contentWidth = [self contentSize].width;

//calc center point which would leave same amount of content on both sides
CGFloat centerY = (contentHeight - [self bounds].size.height) / 2.0;
CGFloat centerX = (contentWidth - [self bounds].size.width) / 2.0;

if (currentOffset.x == 0 && currentOffset.y == 0){
    //assume this is first load. Set offset to content center.
    self.contentOffset = CGPointMake(centerX, centerY);
    return;
}

//distance from center is abs of where we are and where we want to be:
CGFloat offsetY = centerY-currentOffset.y;
CGFloat offsetX = centerX-currentOffset.x;

//now we decide if we want to recenter
int deltaX = 0;
int deltaY = 0;
if (fabs(offsetX) > CELL_WIDTH){

    if (offsetX < 0){
        deltaX = -ceil(offsetX / CELL_WIDTH);
    }else{
        deltaX = -floor(offsetX / CELL_WIDTH);
    }
    offsetX += offsetX + (CELL_WIDTH * deltaX);
    self.contentOffset = CGPointMake(centerX, currentOffset.y);
}
if (fabs(offsetY) > CELL_HEIGHT){

    if (offsetY < 0){
        deltaY = -ceil(offsetY / CELL_HEIGHT);
    }else{
        deltaY = -floor(offsetY / CELL_HEIGHT);
    }

    offsetY += offsetY + (CELL_HEIGHT * deltaY);

    self.contentOffset = CGPointMake(currentOffset.x, centerY);

}
if (fabs(offsetX) > CELL_WIDTH || fabs(offsetY) > CELL_HEIGHT){
    //move subviews to reflect scrolling
    for (UIView *subview in self.subviews) {
        CGPoint center = subview.center;
        center.x += offsetX;
        center.y += offsetY;
        subview.center = center;
    }

    if ([self.infiniteDataModelDelegate conformsToProtocol:@protocol(InfiniteDataModelProtocol)]){
        //moves the data model to reflect "scrolling"
        [self.infiniteDataModelDelegate contentDidChangeModelDeltaX:deltaX deltaY:deltaY];
    }
    [self reloadData];
}
}
Jayesh Thanki
  • 1,879
  • 2
  • 21
  • 29
Jordan
  • 131
  • 1
  • 8
  • Did you find a solution? I am wondering the same thing. – Paul Peelen Mar 21 '13 at 12:08
  • No, at least nothing beyond what I had when I posted, which works but is slow and a little "glitchy". I ended up just using a regular scrollView and making it look like a "collection" view (a grid). It works perfectly, it's just not with a collectionView which was my goal! – Jordan Mar 21 '13 at 22:39
  • I had the same question, however focussed on `UIScrollView`. I got the answer you are looking for.. using `UICollectionView` from user rdelmar. Link: http://stackoverflow.com/a/15553973/406677 – Paul Peelen Mar 25 '13 at 15:25
  • possible duplicate of [View with continues scroll; both horizontal and vertical](http://stackoverflow.com/questions/15549233/view-with-continues-scroll-both-horizontal-and-vertical) – Roman C Mar 26 '13 at 06:04
  • rdelmar's answer was what I was looking for (and if anything that question is a duplicate of mine but for some reason got an answer!) I was able to get the scrollview infinitely scrolling, so @PaulPeelen if you want an answer for UIScrollView I would be happy to share. But I was not able (until rdelmar) to get it working correctly for UICollectionView – Jordan Apr 05 '13 at 18:53
  • I got it working perfectly too, however I have the problem that the UICollectionView seems to think for a milisecond that views are not visible anymore and reuses the indexpaths. I believe that is something Apple also explained in one of their WWDC videos. – Paul Peelen Apr 08 '13 at 07:39

2 Answers2

1

I have put together a library that provides an infinitely scrolling view in all directions. As the user scrolls around, the framework lays out the tiles and lets the delegate know so it can set up the tiles' presentations. As for performance, the framework introduces no lag: full 60 fps.

The framework with a sample app that displays Flickr images in an infinitely scrolling wall is here: https://github.com/vovagalchenko/scroll-for-days. Additionally, here's a video of the sample app in action: https://cloud.box.com/s/d6bgvlot175au5a3jeh5

0

Have you tried this approach?

-(void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.products count] > 8 && indexPath.row == [self.products count] - 8) {
        [self loadMoreItems];
        [self.collectionView reloadData];

    }
}
Paradox
  • 94
  • 6