iOS開發:Swift實現的輪播圖、無限迴圈檢視控制元件

weixin_34247155發表於2017-01-09

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

輪播圖控制元件實現很多,基本都思路有兩種:1、使用UIScrollView + 2-3 個UIImageView的復位使用;2、使用UICollectionView。雖然網上已經有不少優秀的實現,但適逢開始學習Swift,而且這種旋轉木馬式的迴圈展現也有抽離的封裝的價值,於是自己著手實現了基於UICollectionView的輪播圖控制元件CYPicBannerScrollView及支援自定義檢視的迴圈控制元件CYCircularScrollView。先上效果圖:

1874636-b042607eec79025f.gif

安裝

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的可重用特性,讓滾動的效果更加流暢,實現也更為簡單。而為了能夠實現無限迴圈滾動,在資料來源上作了如下處理:

1874636-127269727309ab14.png
無限迴圈

當實際的資料來源容量大於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等分頁控制元件時當前頁數的延後重新整理。相比於重複複製資料來源而達到無限迴圈效果的實現,這種方法一方面節省了記憶體空間,同時也避免了因為某些意外到達邊界時的斷層甚至崩潰。

以上。

相關文章