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等分页控件时当前页数的延后刷新。相比于重复复制数据源而达到无限循环效果的实现,这种方法一方面节省了内存空间,同时也避免了因为某些意外到达边界时的断层甚至崩溃。

以上。

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

推荐阅读更多精彩内容