美文网首页
利用UICollectionView实现无限轮播(Swift)

利用UICollectionView实现无限轮播(Swift)

作者: 乂滥好人 | 来源:发表于2017-01-10 15:50 被阅读634次

    1、主要使用UICollectionView实现无限轮播图,封装成一个控件(集成UIView),方便使用。

    import UIKit
    private let kCycleCellID = "kCycleCellID"
    class RecommendCycleView: UIView {
        /** 数据模型(由外界传值) */
        var cycleModelArr:[CycleModel]? {
            didSet{
                // 1、刷新表格
                collectionView.reloadData()
                // 2、设置page的个数
                pageControl.numberOfPages = cycleModelArr?.count ?? 0
                // 3、默认滚动到中间某一个位置防止用户一开始就往前拉,看不到东西(有点问题,注释掉)
    //            let indexPath = IndexPath(item: (cycleModelArr?.count ?? 0) * 10, section: 0)
    //            collectionView.scrollToItem(at: indexPath, at: .left, animated: false)
                
                // 4.添加定时器(最好新移除在添加)
                removeCycleTimer()
                addCycleTimer()
            }
        }
        /** 定时器 */
        var cycleTime: Timer?
        @IBOutlet weak var collectionView: UICollectionView!
        @IBOutlet weak var pageControl: UIPageControl!
        
    // MARK- 系统回调(从nib中)
        override func awakeFromNib() {
            super.awakeFromNib()
            // 0、设置该控件不随着父控件的拉伸而拉伸(重要)
            autoresizingMask = UIViewAutoresizing()
            // autoresizingMask = .None 3.0之前的写法
            // 1、注册cell
            collectionView.register(UINib(nibName: "CollectionViewCycleCell", bundle: nil), forCellWithReuseIdentifier: kCycleCellID)
        }
        
        // 当控件是从nib中获取的话,尺寸往往是不对的,最好在layoutSubviews中设置尺寸
        override func layoutSubviews() {
            super.layoutSubviews()
            // 设置collectionView的layou
            let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
            layout.itemSize = collectionView.bounds.size
        }
    }
    
    // MARK:- 提供一个快速创建View的类方法
    extension RecommendCycleView {
        class func recommendCycleView() -> RecommendCycleView{
            return Bundle.main.loadNibNamed("RecommendCycleView", owner: nil, options: nil)?.first as! RecommendCycleView
        }
    }
    
    // MARK:- <UICollectionViewDataSource>
    extension RecommendCycleView:UICollectionViewDataSource, UICollectionViewDelegate {
        
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            // 1、需要无限轮播,所以在返回的时候给当前属性添加更多item
            return (cycleModelArr?.count ?? 0) * 10000
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCycleCellID, for: indexPath) as! CollectionViewCycleCell
            // 2、但是如果一直在 cycleModelArr 中取,数组肯定会越界,所以要 % cycleModelArr!.count 防止越界问题
            cell.cycleModel = cycleModelArr![indexPath.item % cycleModelArr!.count]
            return cell
        }
        
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            print("当前点击了第 \(indexPath.item % cycleModelArr!.count) 个Item")
        }
        
        // 监听collectionView的滚到
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            // 1、获取滚动的偏移量 + scrollView.bounds.width * 0.5给偏移量加一半,当滑动一般就滚动pageControl的当前选中
            let offsetX = scrollView.contentOffset.x + scrollView.bounds.width * 0.5
            // 2、计算pageContra的currentIndex。这 % (cycleModelArr?.count ?? 1)也是跟上同样道理
            pageControl.currentPage = Int(offsetX / scrollView.bounds.width) % (cycleModelArr?.count ?? 1)
        }
        
        // 监听当手动拖拽的时候,移除定时器
        func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
            removeCycleTimer()
        }
        
        // 监听当手动拖拽结束时,添加定时器
        func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
            addCycleTimer()
        }
    }
    
    extension RecommendCycleView{
        
        fileprivate func addCycleTimer() {
            cycleTime = Timer(timeInterval: 3.0, target: self, selector: #selector(self.scrollToNext), userInfo: nil, repeats: true)
            // 把定时器加入 运行循环中
            RunLoop.main.add(cycleTime!, forMode: RunLoopMode.commonModes)
        }
        
        /** 删除计时器 */
        fileprivate func removeCycleTimer() {
            cycleTime?.invalidate() // 从运行循环中移除
            cycleTime = nil
        }
        
        // 注意:当 extension 中要给方法加私有修饰词的话,前面必须加 @objc
        @objc fileprivate func scrollToNext() {
            // 1.获取collectionView的X轴滚动的偏移量
            let currentOffsetX = collectionView.contentOffset.x
            let offsetX = currentOffsetX + collectionView.bounds.width
            // 2.滚动该位置
            collectionView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: true)
        }
    }
    

    创建空的Xib,与RecommendCycleView关联



    2、UICollectionViewCell

    import UIKit
    import Kingfisher
    class CollectionViewCycleCell: UICollectionViewCell {
    
        @IBOutlet weak var titleLb: UILabel!
        @IBOutlet weak var iconImageView: UIImageView!
        /** 定义模型属性 */
        var cycleModel:CycleModel? {
            didSet{
                titleLb.text = cycleModel?.title
                guard let iconURL = URL(string: (cycleModel?.imageUrl)!) else {return}
                iconImageView.kf.setImage(with: iconURL, placeholder: Image(named: "Img_default"), options: nil, progressBlock: nil, completionHandler: nil)
            }
        }
        
        override func awakeFromNib() {
            super.awakeFromNib()
            
        }
    
    }
    

    UICollectionViewCell关联的Xib



    3、数据模型

    import UIKit
    /** 轮播图模型 */
    class CycleModel: NSObject {
        /** 标题 */
        var title: String?
        /** 图片地址 */
        var imageUrl: String?
        
        // MARK:- 自定义构造函数
        init(dict: [String: NSObject]) {
            super.init()
            setValuesForKeys(dict)
        }
        override func setValue(_ value: Any?, forUndefinedKey key: String) {
            
        }
    }
    

    新建plist,造假数据



    4、viewModel中模拟网络请求,请求假数据

    // 请求无限录播图数据
        func requestCycleDate(finishCallback: @escaping() -> ()) {
            
            // 1、获取本地plis
            let cycleData = Bundle.main.path(forResource: "CycleView", ofType: "plist")
            let cycleDict = NSDictionary(contentsOfFile: cycleData!)
            // 2、校验
            guard let cycleDic = cycleDict as? [String: NSObject] else {return}
            guard let cycleArr = cycleDic["data"] as? [[String: NSObject]] else {return}
            // 3、数组字典转模型
            for dict in cycleArr {
                let cycle = CycleModel(dict: dict)
                self.cycleModelArr.append(cycle)
            }
            // 4、完成回调。PS:因为轮播图无法请求,所以使用假数据,正常应该是这次位置请求轮播图的数据进行解析储存
            finishCallback()
        }
    

    5、调用!在需要的控制器中懒加载无限轮播控件,前提先懒加载viewModel(专门请求数据的类)

    1>、懒加载viewModel

    /** viewModel */
        fileprivate lazy var recommendVM: RecommendViewModel = RecommendViewModel()
    

    2>、懒加载轮播器

    /** 轮播器(添加到collectionView上) */
        fileprivate lazy var cycleView:RecommendCycleView = {
            let cycleView = RecommendCycleView.recommendCycleView()
            cycleView.frame = CGRect(x: 0, y: -(kcycleViewH + kGameViewH), width: kScreenW, height: kcycleViewH)
            return cycleView
        }()
    

    3>、赋值

    //  通过MVVM的属性调用 请求轮播数据
            recommendVM.requestCycleDate {
                // 传值给轮播View的 cycleModel 的属性,在uiview中给图片赋值
                self.cycleView.cycleModelArr = self.recommendVM.cycleModelArr
            }
    

    相关文章

      网友评论

          本文标题:利用UICollectionView实现无限轮播(Swift)

          本文链接:https://www.haomeiwen.com/subject/lnwkbttx.html