[Swift] CollectionView 輪播

Teddy
Goons
Published in
8 min readJan 12, 2020

在 Xcode 11.3 環境中使用 collectionView 來實作自動無限輪播圖片功能。

一、 原理說明:

在原始陣列最前方加上最後一筆資料以及陣列最後一個位置加上第一筆資料

[UIImage1, UIImage2, UIImage3] // 原始陣列[UIImage3, UIImage1, UIImage2, UIImage3, UIImage1] // 輪播的資料陣列

當畫面載入時,預設顯示第二筆資料(index = 1),接著當滑動到最後一個( index = 5)時,偷偷的把當前位置改成第二個(index = 5 -> index = 1),反之滑動到第一個(index = 0)時,把當前位置改成倒數第二個(index = 0 -> index = 4),用這樣的障眼法來實現出頁面一直滑動可以重複顯示資料的功能。

實作成果:

二、實作

  1. 首先當然要先在 ViewController 中準備以下幾個內容

(1) init CollectionView,並且設定以單頁為單位滑動

(2) 提供一組需要使用的資料陣列

(3) 記錄當前 index 的變數(預設為第二筆資料,即 index = 1)

(4) init 一個 Timer 作為自動輪播的 scheduled

// MARK: init element
private lazy var collectionView: UICollectionView = {
let collectionViewLayout = UICollectionViewFlowLayout() collectionViewLayout.scrollDirection = .horizontal collectionViewLayout.minimumLineSpacing = 0
let view = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) view.register(BannerCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: BannerCollectionViewCell.self)) view.delegate = self view.dataSource = self view.isPagingEnabled = truereturn view}()// MARK: data source
let originalDataSource = [UIImage1, UIImage2, UIImage3]
var sortDataSource = [UIImage]()
var currentIndex = 1// MARK: timer
var timer: Timer?

2. 實作 CollectionView 的 delegate ,這邊我們需要使用到scrollViewDidScroll

// MARK: CollectionView delegate
extension ViewController: UICollectionViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { // 原始資料
let pageFloat = (scrollView.contentOffset.x /scrollView.frame.size.width)
// 無條件捨去
let pageInt = Int(pageFloat)
// 無條件進位
let pageCeil = Int(ceil(pageFloat))
switch pageInt { case 0: setCurrentPage(to: pageCeil) default: setCurrentPage(to: pageInt) }
}
}

3. 實作 CollectionView 的 DataSource

// MARK: CollectionView dataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sortDataSource.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: BannerCollectionViewCell.self), for: indexPath) as! BannerCollectionViewCell cell.infoSet(image: sortDataSource[indexPath.row]) return cell
}
}

4. 在 ViewController 需要使用到的方法

(1) timer 固定時間觸發的發法 ( func update() )

(2) 一直滑動可以重複顯示資料的邏輯方法(func setCurrentIndex( scrollToPage: Int) )

// MARK: active
@objc private func update() {
// 當前頁數 +1
collectionView.setContentOffset(CGPoint(x: CGFloat(Int(UIScreen.main.bounds.width) * (currentIndex + 1)), y: 0), animated: true)
}
@objc private func setCurrentIndex(scrollToPage: Int) {
switch scrollToPage {
case 0:
// 滑到第一筆資料時, 把 index 改成倒數第二筆
collectionView.setContentOffset(CGPoint(x: CGFloat(Int(UIScreen.main.bounds.width) * (sortDataSource.count - 2)), y: 0), animated: false)
case sortDataSource.count - 1: // 滑到最後一筆資料時, 把 index 改成第二筆
collectionView.setContentOffset(CGPoint(x: UIScreen.main.bounds.width, y: 0), animated: false)
default:

// 其餘 index 不做任何動作
break
}
let setIndxe = Int(collectionView.contentOffset.x / UIScreen.main.bounds.width) currentIndex = setIndxe
}

5. 最後在 ViewController 中的 方法 viewDidLoad 中加上初始畫面需要的設置

override func viewDidLoad() {   super.viewDidLoad()   // 記得layout collectionView 到畫面上(請自行處理XD)   guard let bannerFirst = originalDataSource.first,
let bannerLast = originalDataSource.last
else { return }
sortDataSource.append(bannerLast)
sortDataSource += originalDataSource
sortDataSource.append(bannerFirst)
collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.setContentOffset(CGPoint(x: CGFloat(UIScreen.main.bounds.width), y: 0), animated: true)
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(update), userInfo: nil, repeats: true)}

我是果思設計的 iOS 工程師 Teddy,我們專注在 App 設計與 App 開發,希望文章對各位有幫助!非常期待在留言區與大家討論、吸收更多觀點:)

--

--