iOS开发:Swift实现的轮播图、无限循环视图控件

Github:https://github.com/lxypeter/CYCircularScrollView

轮播图控件实现很多,基本都思路有两种:1、使用UIScrollView + 2-3 个UIImageView的复位使用;2、使用UICollectionView。虽然网上已经有不少优秀的实现,但适逢开始学习Swift,而且这种旋转木马式的循环展现也有抽离的封装的价值,于是自己着手实现了基于UICollectionView的轮播图控件CYPicBannerScrollView及支持自定义视图的循环控件CYCircularScrollView。先上效果图:

安装

CocoaPods

pod 'CYCircularScrollView'

CocoaPods版本需要在1.1.0以上

手动引入

你也可以手动将CYCircularScrollView拷贝到项目中,但注意项目中需要同时引入Kingfisher,因为在轮播图控件** CYPicBannerScrollView**中直接使用了Kingfisher框架进行网络图片加载。

如何使用

轮播图控件:CYPicBannerScrollView

1. 初始化

CYPicBannerScrollView支持三种类型的数据源:UIImage图片链接字符串以及数据模型(Model),与之对应的有3种初始化方法。

  • UIImage类型
convenience init(frame:CGRect, images:[Any]?, didSelected:((Int,Any)->())?)
//e.g.
let imageArray = [UIImage(named: "banner_1")!,UIImage(named: "banner_2")!,UIImage(named: "banner_3")!]
let bannerView = CYPicBannerScrollView(frame: CGRect.zero,
                                           images:imageArray ) { (index, data) in
    //click event
}
  • 图片链接字符串
convenience init(frame:CGRect, urlStrings:[Any]?, placeholder:UIImage?, didSelected:((Int,Any)->())?)
//e.g.
let urlArray = ["www","www","www"]
let bannerView = CYPicBannerScrollView(frame: CGRect.zero,
                                       urlStrings: urlArray,
                                       placeholder: UIImage(named: "pic_placeholder")) { (index, data) in
    //click event
}
  • 数据模型(Model)
convenience init(frame:CGRect, models:[Any]?, placeholder:UIImage?, modelImage:@escaping (Any)->(CYImageResult), didSelected:((Int,Any)->())?) 
//e.g.
let announceArray = [Announcement(title:"First Announcement",time:"2017-01-01",image:"p1"),
                    Announcement(title:"Second Announcement",time:"2017-01-02",image:"p2"),
                    Announcement(title:"Third Announcement",time:"2017-01-03",image:"p3")]
let bannerView = CYPicBannerScrollView(frame: CGRect.zero,
                                       models: announceArray,
                                       placeholder: UIImage(named: "pic_placeholder"),
                                       modelImage: { (model) -> (CYImageResult) in
    let announcement = model as! Announcement
    return CYImageResult(data: UIImage(named: announcement.image)!, type: .image)
}) { (index, data) in
    //click event
}

对于对象模型类型的初始化,需要在modelImage闭包中根据数据模型返回图片数据源,支持直接返回UIImage如:CYImageResult(data: UIImage(named: model.image)!, type: .image)或链接字符串如:CYImageResult(data: "http://image.com/image", type: .urlString)

2. 自定义样式

CYPicBannerScrollView支持以下形式的客制化

bannerView.autoScrollInterval = 3.0 //自动翻页间隔,默认为5秒

bannerView.pageControlPosition = .right //PageControl的位置,默认为center
bannerView.pageControlOffset = UIOffset(horizontal: -10, vertical: -5) //PageControl的基于位置的偏移量

//自定义PageControl的样式
bannerView.pageControl.backgroundColor = UIColor(displayP3Red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 0.8)
bannerView.pageControl.layer.cornerRadius = 8

支持自定义视图的循环控件:CYCircularScrollView

要实现自定义视图循环展示只需要四步

  • 1.继承 CYCircularScrollView
class CYAnnounceScrollView : CYCircularScrollView
  • 2.覆写var cellClass:UICollectionViewCell.Type属性
override var cellClass:UICollectionViewCell.Type {
    return CYAnnounceCell.self //循环的视图类型,需为UICollectionViewCell子类
}
  • 3.覆写func configureCollectionCell(_ cell:UICollectionViewCell, data:Any) -> UICollectionViewCell方法
override func configureCollectionCell(_ cell:UICollectionViewCell, data:Any) -> UICollectionViewCell {
        
    let announceCell = cell as! CYAnnounceCell
        
    if let announcement:Announcement = data as? Announcement{
        announceCell.announcement = announcement
    }
        
    return announceCell
}
  • 4.根据需求覆写属性
override var scrollDirection:CYScrollDirection {
    return .vertical//滚动方向,默认为.horizontal
}
    
override var isScrollEnabled:Bool {
    return false//是否可以手动滚动,默认为true
}

此时你就能使用你自定义的视图控件了~

let annouceScrollView = CYAnnounceScrollView(frame: CGRect.zero, datas: self.announceArray) { (index, data) in
    //click event
}

更详尽的使用可以参照Github上的Demo。

实现思路

CYCircularScrollView是以UICollectionView为基础的,借助UICollectionView的可重用特性,让滚动的效果更加流畅,实现也更为简单。而为了能够实现无限循环滚动,在数据源上作了如下处理:

无限循环

当实际的数据源容量大于1时,UICollectionView接收的数据量加2,在前后增添各一缓冲视图,用以优化到达数据源边界时的滚动效果。同时监听UICollectionView代理下的滚动事件public func scrollViewDidScroll(_ scrollView: UIScrollView)

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if self.dataArray == nil {
            return
        }
        
        var index:Float
        switch self.scrollDirection {
        case .horizontal:
            index = Float(scrollView.contentOffset.x * 1.0 / scrollView.frame.size.width)
        case .vertical:
            index = Float(scrollView.contentOffset.y * 1.0 / scrollView.frame.size.height)
        }
        
        if index < 0.25 {
            self.collectionView.scrollToItem(at: IndexPath(item: self.dataArray!.count, section: 0), at: [.top,.left], animated: false)
        }else if index >= Float(self.dataArray!.count+1) {
            self.collectionView.scrollToItem(at: IndexPath(item: 1, section: 0), at: [.top,.left], animated: false)
        }
        
        let page = self.transferIndex(Int(index))
        
        scrollToPage(page)
}

UICollectionView滚动至首项(实际数据源末项的缓冲视图)及尾项(实际数据源首项的缓冲视图)并即将展示完时,UICollectionView立即以无动画效果跳转至缓冲视图对应的实际项所在,以尽可能少的掉帧达到无限循环的效果。而当前页数的监听与刷新也在该步完成,避免了当存在UIPageControl等分页控件时当前页数的延后刷新。相比于重复复制数据源而达到无限循环效果的实现,这种方法一方面节省了内存空间,同时也避免了因为某些意外到达边界时的断层甚至崩溃。

以上。

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

推荐阅读更多精彩内容