Reputation: 141
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];
}
}
Upvotes: 4
Views: 4977
Reputation: 79
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
Upvotes: 1
Reputation: 94
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];
}
}
Upvotes: 0