轮播大法——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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

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