轮播大法——SDCycleScrollView 源码思路解析

SDCycleScrollView 轮播图源码解析

一、开篇

轮播图控件,在 iOS 应用上是有很多应用的。

这个第三方框架,在 github 上有 4000 个 star。

出于好奇,研究了一下。

不要看,这是有 4000 个 star 的第三方库,它的实现原理其实是可以用一句话概括的:

UICollectionView 的重用机制 和 数学的 取模运算

总体思路:

利用 UICollectionView 的重用机制,保证图片切换的流畅。

利用重用机制,在设计的时候,有意让 item 的数量是需要展示的图片数量的 100 倍。

例如:需要轮播的图片数量是 5 张,那么 UICollectionView 的 item 的总数 _totalItemsCount 其实是 5X100 = 500 个。

在初始化布局子控件的时候,laySubview 方法里面直接是调用:

scrollToItemAtIndexPath: atScrollPosition:animated: 方法,

设置默认的显示图片的 item 是 _totalItemsCount X 0.5 ,也就是第 250 个,对应显示数组里面的第 0 张图片。

500 个 item 的排列大概是这样的:

0 1 2 3 4; 0 1 2 3 4;........ 0 1 2 3 4; 0 1 2 3 4;

一共有 100 组 0 到 4 的数组图片。

乘以 0.5 ,是为了可以显示在 500 item 的中间位置。

这样的话,左右滑动都是可以看到 item 的。

这个框架里面严格来说:只是实现了永久自动循环轮播,没有实现永久手动循环轮播。

这里用语言描述实在是太困难了。

所以,我索性把代码敲了一遍,相较于原有框架更容易理解清楚。

点击这里,下载示例代码

二、 SDCycleScrollView 类详解

SDCycleScrollView 框架里面最重要的类就是 SDCycleScrollView ,理解了这个类,基本思想就完全可以搞定。

下面开始介绍。

SDCycleScrollView 继承自 UIView ,本质是一个 UIView 。

我的代码里面与框架里面对应的类名是: WPCycleScrollView。

请读者下载我的代码,对照阅读。

下面是 WPCycleScrollView.m 文件的

1、主要属性:

      @interface WPCycleScrollView ()              <UICollectionViewDelegate,UICollectionViewDataSource>

      @property (nonatomic, weak) UICollectionView *mainView;// 显示图片的 collectionView

      @property (nonatomic, weak) UICollectionViewFlowLayout *flowLayout;// 布局属性

      @property (nonatomic, strong) NSArray *imageGroup;//图片数组

      @property (nonatomic, assign) NSInteger totalItems;// item 的数量

@property (nonatomic, weak) NSTimer *timer;// 定时器

@end

2、布局子控件的方法:

- (void)layoutSubviews {
    
    [super layoutSubviews];
    
    _mainView.frame = self.bounds;

    if (_mainView.contentOffset.x == 0 && _totalItems > 0) {
        NSInteger targeIndex = 0;
        if (self.infiniteLoop) {//无限循环
            // 如果是无限循环,应该默认把 collection 的 item 滑动到 中间位置。
            // 注意:此处 totalItems 的数值,其实是图片数组数量的 100 倍。
            // 乘以 0.5 ,正好是取得中间位置的 item 。图片也恰好是图片数组里面的第 0 个。
            targeIndex = _totalItems * 0.5;
        }else {
            targeIndex = 0;
        }

        //默认的图片位置
        [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targeIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
    }
}

注意:

默认显示的是 UICollectionView 的中间的 item,这样就避免了一开始滑动就滑到了尽头。

这是设计巧妙的地方。

3、设置图片数组,同时设置 item 数量

- (void)setImageGroup:(NSArray *)imageGroup {
    
    _imageGroup = imageGroup;
    
    // 这一句是关键,如果是无限循环,就让 item 的总数乘以 100 。
    
    _totalItems = self.infiniteLoop ? imageGroup.count * 100 : imageGroup.count;
    
    if (_imageGroup.count > 1) {
        self.mainView.scrollEnabled = YES;
        //处理是否自动滑动,定时器问题
        [self setAutoScroll:self.autoScroll];
    }else{
        self.mainView.scrollEnabled = NO;
        [self setAutoScroll:NO];
    }
    
    
    
    [self.mainView reloadData];
    
}

4、计算当前的页码

    - (NSInteger)currentIndex {
        
        if (_mainView.frame.size.width == 0 || _mainView.frame.size
            .height == 0) {
            return 0;
        }
        
        NSInteger index = 0;
        
        if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {//水平滑动
            index = (_mainView.contentOffset.x + _flowLayout.itemSize.width * 0.5) / _flowLayout.itemSize.width;
        }else{
            index = (_mainView.contentOffset.y + _flowLayout.itemSize.height * 0.5)/ _flowLayout.itemSize.height;
        }
        return MAX(0,index);
    }

注意:

此处的运算方法。都是加了 0.5 的。这里是实现四舍五入的计算。
也就是说,偏移量一旦大于一半的宽度或者高度,就增加或者减少一个页码。

5、item 方法

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    WPCycleScrollViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];

    // 利用取余运算,使得图片数组里面的图片,是一组一组的排列的。
    long itemIndex = [self pageControlIndexWithCurrentCellIndex:indexPath.item];
    
    cell.img = [UIImage imageNamed:self.imageGroup[itemIndex]];
    
    return cell;
    
}

- (int)pageControlIndexWithCurrentCellIndex:(NSInteger)index {
    return (int)index % self.imageGroup.count;
}

上面的方法是设置,UICollectionView 的 item 。

需要注意的是:

这里的 % 运算,使得图片数组里面的图片,可以实现
0 1 2 3 4; 0 1 2 3 4 .... 的循环

具体细节,还需要各位去体会。我已经把代码精简了。应该是一目了然。

三、小结

不得不佩服,作者对于 iOS 重用机制的灵活应用。

最难理解的部分,其实是为什么作者要把 item 的数量,在需要显示的图片数量的基础上乘以 100 呢?

其实,我自己测试过,只要是 10 的倍数都是可以的。

你可以自己乘以 10 或者 1000 ,甚至是 10000 都是没有问题。

这里的数值大小,根本不会影响 APP 最好的渲染。

因为,重用机制,并不会同时生成那么多 item,而是循环利用。

在我看来,最少只要两个 item ,其实已经实现了 item 的切换。

这个原理和 UITableView 的 cell 重用机制是一样的。

只要是 10 的倍数就是可以的,这样是便于乘以 0.5 的时候,定位的 item 刚好是数组图片的第一个。

这个很巧妙。欢迎大家,和我交流。

再次放上我的代码,如果大家觉得写得不错,请 star 一下。谢谢。

[点击这里,下载示例代码](https://github.com/wuxiaopei/
WPCycleScrollView)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,352评论 25 709
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,205评论 4 61
  • 最初的流年,我们之间是陌生的,我们以微笑对待彼此;时间使我们之间渐渐变得熟悉,我们成为的好朋友.慢慢的,我们...
    寂寞笙暖情阅读 1,105评论 0 0
  • 姐姐说,你咋对自己的脸下那么大的狠手? 真是心狠手辣 随你妈!
    山山和川川阅读 1,014评论 0 0
  • 有一种现象,在我们的身边很容易出现。就是明明是关心你的人,却又常常贬低讽刺你,具有超强的摧毁别人自信的力量。这个人...
    aimeelala阅读 3,025评论 2 1