UICollectionView horizontal continuous loop

2020-03-01 19:59发布

问题:

So I have a UICollectionView with custom UIViews for each cell. The UIView takes up the whole screen and you can horizontally navigate to each of the 4 different UIView/cells. This all works fine and uses a pager to indicate which page of the collectionview you are on.

I have looked around google and the like to see if I could get it to loop back to the beginning after you navigate passed the last cell. So for example:

I start at the 3rd cell, navigate to the 4th, then when i swipe to the right, it will go back to the first cell (pager will reflect that you are on the first page) and continue on from there.

Is this easy to do? Or am I missing something?

Thanks, Jake

回答1:

You only need 3 UIViews, to hold the image to the left of your current view, the image to the right of the current view, and the middle view, the one that is currently onscreen.

So lets say we have UIImages A B C D and UIViews 1 2 and 3

We are looking at View 2, image B. Page left will take us to image A, page right to image C.

As you swipe left/ page right, view 3 becomes the onscreen view with image C. When paging comes to rest, you swap the views contents around so the user is actually looking at the middle UIView2 again, with image C. View 1 has image B, view 3 has image D.

Scroll right again, and do the same shuffle. Now you have

View 1 -> image C
View 2 -> image D
View 3 -> image A

Next page right

View 1 -> image D
View 2 -> image A
View 3 -> image B

so on, ad infinitum in either direction

There is a nice WWDC video on this subject from 2011. Its worth digging out. It's the UIScrollView demo video (Documentation as PDF) You don't need Collection Views to do this, although there is no reason why you shouldn't...



回答2:

Alright ready for this hotness?

So in my main view controller's viewDidLoad I identify the views to load into the uicollectionview cells in an array:

NSArray *vcs = [@"view5", @"view1", @"view2", @"view3", @"view4", @"view5", @"view1"]

I also set our pagecontrol count to:

self.pageControl.numberOfPages = vcs.count - 2; // for the fake first and last cells

I then instantiate the viewcontrollers in the main view controller's viewDidLoad as well as set the alpha for the collectionview to 0. Then in our scrollViewDidEndDecelerating I handle the Carousel (go from last cell to first and first cell to last) and updating the pageControl to reflect the "real" page number.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    // Snaps into the cell    
    CGFloat pageWidth = scrollView.frame.size.width;
    float fractionalPage = scrollView.contentOffset.x / pageWidth;
    NSInteger page = lround(fractionalPage);

    self.pageControl.currentPage = page - 1; // because our first cell is the last cell for animation purposes only

    // Carousel from first to last and last to first while updating pagecontrol
    if (page == 0) {
        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.activeGaugeClusters.count - 2 inSection:0]
                                    atScrollPosition:UICollectionViewScrollPositionLeft
                                            animated:NO];
        // Set our pagecontrol circles to the appropriate page indicator        
        self.pageControl.currentPage = self.activeGaugeClusters.count - 2;

    } else if (page == self.activeGaugeClusters.count -1) {
        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]
                                    atScrollPosition:UICollectionViewScrollPositionLeft
                                            animated:NO];
        self.pageControl.currentPage = 0;
    }
}

Then in the collectionview implementation I do the following:

numberOfItemsInSection = vcs.count

and viewDidAppear for loading the first cell (whether it is the cell at indexPath.row == 1 or 2 or 3... (1 being the first real page not 0)

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.lastGaugePanel inSection:0]
                                atScrollPosition:UICollectionViewScrollPositionLeft
                                        animated:NO];
    self.pageControl.currentPage = self.lastGaugePanel - 1;
    [UIView animateWithDuration:0.5 animations:^{
        self.collectionView.alpha = 1.0;
    }];
}

I think that about sums it up. The rest of the implementations for both the main vc and the collectionview are standard. So now I have a collection view that loads up other views and their VCs that each have their own animation or whatever. I save the indexPath.row to NSUserDefaults so that the cell that I was in when I leave is the first cell to show on the next load.

Hopes this helps someone, I know it did for me, unfortunately the use of 3rd party libraries was discouraged so I had to build this bad boy up (with help from @He Was, thanks again!)