設定 UICollectionViewFlowLayout 的itemSize,實現一排顯示固定數量的 cell
使用 collection view 我們可以快速做出照片牆,不過如果我們希望一排顯示固定數量的 cell,像 iOS 的照片 App 一樣,不管在直向或橫向都顯示 5 個 cell,要怎麼做到呢?
最簡單的方法是利用 UICollectionViewCompositionalLayout,因為它可以幫我們算出一排 5 個時,一個 cell 需要的大小。
不過本篇文章的主角是 UICollectionViewFlowLayout,它的 cell 大小是由 itemSize 決定,因此接下來我們將說明要在何時更新 itemSize。
假設我們想要呈現的 cell 是正方形,因此若要一排 5 個,而且 0 間距, cell 的寬度將是 collection view 的寬度 / 5。
在 viewDidLoad 計算 cell 的大小 ?
範例1: 使用 collection view controller 顯示整個頁面
collection view 的寬度在 viewDidLoad 時即是正確的寬度,因此可在此計算 cell 的寬度。
class CollectionViewController: UICollectionViewController {
func configureCellSize() {
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = .zero
layout?.minimumInteritemSpacing = 0
let width = floor(collectionView.bounds.width / 5)
layout?.itemSize = CGSize(width: width, height: width)
}
override func viewDidLoad() {
super.viewDidLoad()
configureCellSize()
}
畫面在直向時很完美,一排剛好 5 個。
那如果換成橫向呢 ? 因為 viewDidLoad 只會執行一次,它不會在橫向時重新計算 cell 大小,所以下圖的 cell 大小將是原本直向時計算的大小。
範例2: 使用 view controller + collection view
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
func configureCellSize() {
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = .zero
layout?.minimumInteritemSpacing = 0
let width = floor(collectionView.bounds.width / 5)
layout?.itemSize = CGSize(width: width, height: width)
}
override func viewDidLoad() {
super.viewDidLoad()
configureCellSize()
}
連直向都不對 ! 為什麼呢 ?
因為要等到 viewDidLayoutSubviews 或 viewDidAppear 時,元件的位置大小才會是 Auto Layout 條件算出的位置大小,因此我們在 viewDidLoad 得到的 collection view 寬度是錯的。
解法: 從螢幕寬度計算 collectionView寬度,在 viewWillTransition 時更新 itemSize
剛剛我們看到了兩個問題:
- 取得錯誤的 collection view 寬度。
- 轉向時沒有重新計算 itemSize。
有很多方法可以取得正確的 collection view 寬度,其中一個方法是利用螢幕寬度和畫面設計的間距條件計算。在剛剛的例子裡,collection view 對齊螢幕的左右兩側,因此 collection view 寬度就是螢幕寬度。
畫面轉向時會觸發 view controller 的 viewWillTransition(to:with:),因此我們可定義它,在裡面更新 itemSize。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
configureCellSize(collectionViewWidth: size.width)
}
func configureCellSize(collectionViewWidth: CGFloat) {
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = .zero
layout?.minimumInteritemSpacing = 0
let width = floor(collectionViewWidth / 5)
layout?.itemSize = CGSize(width: width, height: width)
}
override func viewDidLoad() {
super.viewDidLoad()
configureCellSize(collectionViewWidth: UIScreen.main.bounds.width)
}
說明
在 viewDidLoad 呼叫 configureCellSize,此時的 collection view 寬度傳入 UIScreen.main.bounds.width。在畫面轉向,viewWillTransition 觸發時也會呼叫 configureCellSize,此時的 collection view 寬度傳入 size.width,因為 size.width 代表轉向後的螢幕寬度。