iOS 使用UICollectionView实现轮播图

一般来说实现轮播图的基础控件有两个,UIScrollView或UICollectionView,二者选一,我更愿意用UICollectionView,至于原因,读者们发挥想象力吧,我只是在看轮播图的时候第一个就想到了UICollectionView,仅此而已。

既然第一个想到的是UICollectionView,那它对我来说应该就是最合适的。思考一下下面的问题,然后撸代码吧。


问题1
怎样实现顺滑的滚动,使得从最后一个右滑时很自然的显示第一个以及从第一个左滑时很自然的显示最后一个?

首先,我们来分析一下这个问题。
假设我们有n个item要轮播(n >= 1),之所以n不取0,是因为这样没什么意义。
分情况讨论:
1、n <= 1
这个情况最简单嘛,始终只显示一个item或者啥都不显示,既不需要左滑也不需要右滑。

2、n > 1
– 由问题“第一个item[0]向右滑动显示最后一个item[n-1]” 得出:item[0]之前有个item,它就是item[n-1]。
– 由问题“最后一个item[n-1]向左滑动显示第一个item[0]”得出:item[n-1]后面有个item,它就是item[0]。
UI上的item的顺序如下图
这里写图片描述

因此为了解决问题1,在设置items时我们得出了如下逻辑
if (items.count > 1) {
//在数组的头部插入最后一个item,在数组的尾部加上第一个item
//使得新的数组元素个数等于原数组元素个数+2
}

demo代码如下:

- (void)setItems:(NSArray *)items
{
    if (items.count > 1) {
        NSMutableArray  * arr = [items mutableCopy];
        [arr addObject:[items firstObject]];
        [arr insertObject:[items lastObject] atIndex:0];
        _items = arr;
    } else {
        _items = [items copy];
    }
}

问题2
怎样控制item切换间隔?

最容易想到的方式就是NSTimer啦,间隔timeSpace秒执行一次切换操作,滚动到下个item。
说起来容易,但是实现起来稍有麻烦,因为在解决问题1的时候咱们对items数组进行了修改,但是不要怕,因为原理很简单。下面分情况讨论一下:

1、从items[0]右滑切到items[n-1],如下图
这里写图片描述
当滚动结束,item[n-1]完全显示后,我们需要手动的修改collectionView的偏移量,显示红色框框的那个item[n-1]。
这个偏移量也很好计算width * ( _items.count - 2)

2、从items[n-1]左滑切到item[0],如下图
这里写图片描述
当滚动结束,item[0]完全显示后,我们也需要手动的修改collectionView的偏移量,显示红色框框的那个item[0]。
这个偏移量更容易看出来,就是width自己了

第二个问题也搞定了,还有什么问题呢?
到目前为止,搞定了这两个问题之后,一个简单的轮播图已经完成了。



然而,作为一枚追求完美的猿,怎么可以这样草草了事呢?而且做的又不是国企的项目~



优化1

添加一个小点点的控件,用来表示当前显示的是第几个item。

这个就是UIpageControl啦。

timer方法执行的时候,很好计算下一个要显示的哪个item,这种情况下设置currentPage并不难,所以在这里就略过了。

当手指滑动切换item的时候,我们也要得到将要显示的是哪个item,在此我要告诉你一个UIScrollView的代理方法

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{
    //targetContentOffset->x即为scrollView在x轴的目标偏移量
    //x/width就是目标索引了,但是要记得转化成实际的索引。
}

通过这个代理方法我们能预先知道collectionView将要停在哪里,这样的话我们只需要那这个坐标的x除以其宽度,就可以知道索引了。

详细的处理过程在这里就不说了,有兴趣的话可以看代码。文章最后我会放上代码链接。


优化2

timer控制的自动滚动方法与手势滚动的冲突

在这里我学习了网易云音乐的效果,实现方式如下:
当有手势的时候就把timer关掉;
当滚动完成后再启用timer;
为了让当前这个item显示时间达到timeSpace秒,使用了GCD的dispatch_after延迟timeSpace秒后fire timer。

此项优化会导致一个问题,如果连续滑动,会导致调用了好几次dispatch_after,导致所做的延迟没有意义。因此我使用了NSInvocationOperation来做相关控制,理由很简单,因为我可以随时cancel掉一个不想要的操作。


优化3

像网易云音乐一样,我不想用户滑得很快,在一个item还未显示完全就开始下一次的滑动

这个问题可以通过userInteractionEnabled这个属性控制。

最后附上代码地址,大家可以拿去直接用,当然点击回调代码中未实现,需要自行添加。喜欢的话记得star哦

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值