利用UICollectionView實現無限輪播(Swift)

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- 系統(tǒng)回調(從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:- 提供一個快速創(chuàng)建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")
    }
    
    // 監(jiān)聽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)
    }
    
    // 監(jiān)聽當手動拖拽的時候,移除定時器
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        removeCycleTimer()
    }
    
    // 監(jiān)聽當手動拖拽結束時,添加定時器
    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)
        // 把定時器加入 運行循環(huán)中
        RunLoop.main.add(cycleTime!, forMode: RunLoopMode.commonModes)
    }
    
    /** 刪除計時器 */
    fileprivate func removeCycleTimer() {
        cycleTime?.invalidate() // 從運行循環(huán)中移除
        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)
    }
}

創(chuàng)建空的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
        }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評論 25 708
  • 6.請求輪播數據 6.1網絡請求繼續(xù)封裝在ViewModel中 懶加載一個輪播模型數據 發(fā)送網絡請求 6.2 回到...
    whong736閱讀 529評論 0 0
  • 發(fā)現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,703評論 4 61
  • 串聯,換一個順序來改善你的效率。要事第一,瑣事休息中處理。最近有個壞習慣晚上聽書前,先處理一下瑣事,刷會資訊和朋友...
    水中望我閱讀 192評論 0 0
  • 苦苦等待 一個南部濱海城市變冷 等到心力交瘁 等漫天的蕭瑟 遲遲不來 等皮膚上冰雪的涼意 遲遲不來 苦苦等待 仿佛...
    蔣菱閱讀 181評論 2 1

友情鏈接更多精彩內容