网上找到了使用scrollView实现的无限轮播的方法,需要自己写一个缓存池,不过没有找到collectionView的方法,在这边使用CollectionView实现,下面的demo本人跑起来完全没问题,如有问题的地方,希望大家指出来(里面有一些方法是自己封装的基类,会注入说明)。
基本思路:在原有的数组最前面再插入一个最后的数据,在最后面插入一个最前面的数据,这有点类似于将 12345 变成 5123451
代码:
首先是实现collectionView的数据源方法:
//每个section有多少个item
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.circularRollViewArray.count;
}
//返回每个cell,这里的CirularRollViewCell是一个自定义cell,里面就是一个简单的imageview,有想法的同学可以做的复杂一点。
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CirularRollViewCell *cell = nil;
//从缓存池中取cell。
cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([CirularRollViewCell class]) forIndexPath:indexPath];
//这里的viewUpdateWithModel方法是我自己写的协议方法,就是通过数据更新视图的方法,这边你们知道就行了。
if ([cell respondsToSelector:@selector(viewUpdateWithModel:)]) {
[cell viewUpdateWithModel:self.circularRollViewArray[indexPath.item]];
}
//这一步是实现代理的方法,不写对整个轮播的实现没影响。
if ([cell respondsToSelector:@selector(setDelegate:)]) {
[cell performSelector:@selector(setDelegate:) withObject:self];
}
return cell;
}
然后在viewUpdateWithModel方法中实现重组数组,在重组之后重加在,然后将视图跳到重组过后的数组的第二个数据,即原本的第一个数据,然后启动定时器
[self.circularRollViewArray addObject:[self.imageArray lastObject]];
[self.circularRollViewArray addObjectsFromArray:self.imageArray];
[self.circularRollViewArray addObject:[self.imageArray firstObject]];
[self.circularRollView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:0];
[self.circularRollView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
//开始定时器其实就是把上一次的定时器销毁,然后再重新创建一个新的定时器
[self startTimer];
下面的很关键,就是如何跳转的逻辑
//由于collectionView没有类似于viewDidload这样的方法,只能寄托于下面的这个代理方法。这个代理方法是在cell将要出现的时候就调用一次。
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
//记录将要出现的cell,来作为下面的是否换cell做判断。
self.nextItem = indexPath.item;
}
//这个方法是上一个视图完全消失,就调用一次
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
//按照5123451来做判断,如果当前的内容是第一个5,则直接无动画的转到倒数第二个的5。如果当前内容是最后一个1,则无动画的跳转到正数第二个1。
if (indexPath.item == 1 && self.nextItem == 0) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForItem:(self.circularRollViewArray.count - 2) inSection:0];
[self.circularRollView scrollToItemAtIndexPath:tmpIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}else if ((indexPath.item == (self.circularRollViewArray.count - 2)) && (self.nextItem == (self.circularRollViewArray.count - 1))){
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForItem:1 inSection:0];
[self.circularRollView scrollToItemAtIndexPath:tmpIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}else{
}
if(indexPath.item == self.circularRollViewArray.count - 1){
self.endDisplayItem = 0;
}else{
}
}
// 通过这个方法,来控制currentPage
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSInteger pageNum = ((scrollView.contentOffset.x + (kscreenWidth / 2.0)) / kscreenWidth);
if (pageNum == 0) {
self.pageControl.currentPage = self.circularRollViewArray.count - 2;
}else if (pageNum > 0 && pageNum < (self.circularRollViewArray.count - 1)){
self.pageControl.currentPage = pageNum - 1;
}else{
self.pageControl.currentPage = 0;
}
}
//这里是定时器所调用的方法,每调用一次,都向前一张跳转。这里为什么使用self.endDisplayItem而不是使用self.nextItem呢?原因是当你手动拖动的时候,只要稍微拖动一下,就会改变self.nextItem的值,等到定时器下次跳转的时候就会跳转两张,用户体验不是很好。
- (void)circularRollViewToNextItem
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:(self.endDisplayItem + 2) inSection:0];
[self.circularRollView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self endTimer];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self startTimer];
}
大致的核心思想差不多就是那些,下面是一些控件创建的方法
//下面是collectionView的初始化和timer的初始化
//这里的_GETTER_BEGIN是自己写的一个宏定义,你们理解就好了。还有一点,当这个view被销毁的时候,一定要记得销毁定时器。
_GETTER_BEGIN(UICollectionView, circularRollView)
{
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.minimumLineSpacing = 0;
flowLayout.itemSize = CGSizeMake(kscreenWidth, (self.CircularRollViewHeight ? self.CircularRollViewHeight : kdefineViewHeight));
_circularRollView = [[UICollectionView alloc] initWithFrame:CGRectMake(0,0,kscreenWidth , kdefineViewHeight) collectionViewLayout:flowLayout];
_circularRollView.showsHorizontalScrollIndicator = NO;
_circularRollView.bounces = NO;
_circularRollView.delegate = self;
_circularRollView.dataSource = self;
_circularRollView.pagingEnabled = YES;
_circularRollView.backgroundColor = kclearColor;
[_circularRollView registerClass:[CirularRollViewCell class] forCellWithReuseIdentifier:NSStringFromClass([CirularRollViewCell class])];
}
_GETTER_END(circularRollView)
_GETTER_BEGIN(NSTimer, timer)
{
_timer = [NSTimer scheduledTimerWithTimeInterval:3.f target:self selector:@selector(circularRollViewToNextItem) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
_GETTER_END(timer)
- (void)endTimer
{
[self.timer invalidate];
self.timer = nil;
}
- (void)startTimer
{
[self endTimer];
[self timer];
}
只视图的搭建这就靠大家自己搭建了,我写的demo里面,pageControl用的就是原生的UIPageControl,不过在外面留一个可以自定义位置的属性。如果大家有什么疑问的地方,可以加我的QQ问我。以后有什么好的方法也会分享给大家。
本论文只是用来学习用的,无限轮播还是有第三方框架的:iCarousel https://github.com/nicklockwood/iCarousel。使用起来也非常的简单