利用 UIBackgroundConfiguration 在 collection view cell 顯示圖片

開發 iOS App 時,我們經常使用 collection view 實作格子狀照片牆跟水平滑動的分頁照片牆。從前我們必須自訂 collection view cell 類別,然後加入顯示圖片的 image view。

在 iOS 15 我們有更簡單的方法。 透過 UIBackgroundConfiguration 的 property image & imageContentMode,我們可以設定 cell 顯示的圖片,不用再額外加入 image view 了。

image & imageContentMode 是 iOS 15 新增的 property,因此要 iOS 15 才支援。

接下來我們將示範如何利用 UIBackgroundConfiguration 實現四種常見的 App 畫面。

1. 格子狀照片牆。
2. 水平滑動的全螢幕分頁照片牆。
3. 不分頁的水平滑動照片牆。
4. 顯示網路圖片的格子狀照片牆。

格子狀照片牆

在 assets 加入圖片

設定 cell ID

  • 方法1: 從 Interface Builder 加入 cell,設定它的 cell ID。
  • 方法2: 呼叫 register 設定 cell ID。
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")

}

到時候 cell 將從程式產生,因此 Interface Builder 的 collection view 不須加入 cell。

設定 cell 大小

假設一排 3 個 cell。

class CollectionViewController: UICollectionViewController {

func configureCellSize() {
let itemSpace: CGFloat = 3
let columnCount: CGFloat = 3
let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout
let width = floor((collectionView.bounds.width - itemSpace * (columnCount-1)) / columnCount)
flowLayout?.itemSize = CGSize(width: width, height: width)
flowLayout?.estimatedItemSize = .zero
flowLayout?.minimumInteritemSpacing = itemSpace
flowLayout?.minimumLineSpacing = itemSpace

}

override func viewDidLoad() {
super.viewDidLoad()
configureCellSize()
}

設定 cell 數量

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 21
}

使用 UIBackgroundConfiguration 設定圖片

UIBackgroundConfiguration 的 image 設定顯示的圖片,imageContentMode 設定圖片縮放的模式,scaleAspectFill 將讓圖片維持比例填滿 cell。

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.image = UIImage(named: "pic\(indexPath.item)")
backgroundConfiguration.imageContentMode = .scaleAspectFill
cell.backgroundConfiguration = backgroundConfiguration

return cell
}

結果

透過 UIBackgroundConfiguration 我們還可設定許多特別的效果,比方以下例子。

  • 設定圓角

設定 cornerRadius。

var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.image = UIImage(named: "pic\(indexPath.item)")
backgroundConfiguration.imageContentMode = .scaleAspectFill
backgroundConfiguration.cornerRadius = 20
  • 設定圓角,邊框寬度,邊框顏色,圖片跟 cell 邊界的間距

backgroundInsets 可設定背景的內容跟 cell 上下左右邊界的間距。

var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.image = UIImage(named: "pic\(indexPath.item)")
backgroundConfiguration.imageContentMode = .scaleAspectFill
backgroundConfiguration.cornerRadius = 20
backgroundConfiguration.strokeWidth = 5
backgroundConfiguration.strokeColor = .black
backgroundConfiguration.backgroundInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

水平滑動的分頁照片牆

在 assets 加入圖片跟設定 cell ID

參考格子狀照片牆的說明。

設定 collection view 的捲動方向為水平,勾選 Paging Enabled

Scroll Direction 設為 Horizontal,勾選 Paging Enabled。

設定 cell 大小為螢幕大小

由於 controller 繼承 UICollectionViewController,因此 collectionView.bounds.size 即為螢幕的大小。

class CollectionViewController: UICollectionViewController {

func configureCellSize() {
let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout
flowLayout?.itemSize = collectionView.bounds.size
flowLayout?.estimatedItemSize = .zero
flowLayout?.minimumInteritemSpacing = 0
flowLayout?.minimumLineSpacing = 0

}

override func viewDidLoad() {
super.viewDidLoad()
configureCellSize()

}

設定 cell 數量

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 21
}

使用 UIBackgroundConfiguration 設定圖片

imageContentMode 設為 scaleAspectFit,圖片將維持比例顯示,上下或左右留白。

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.image = UIImage(named: "pic\(indexPath.item)")
backgroundConfiguration.imageContentMode = .scaleAspectFit
cell.backgroundConfiguration = backgroundConfiguration

return cell
}

結果

不分頁的水平滑動照片牆

我們也可以實現不分頁的水平滑動照片牆,比方以下例子。

設定 cell 的大小為 100 * 100,間距為 10。

使用 UIBackgroundConfiguration 設定圖片。

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.imageContentMode = .scaleAspectFill
backgroundConfiguration.image = UIImage(named: "pic\(indexPath.item)")
cell.backgroundConfiguration = backgroundConfiguration


return cell

}

顯示網路圖片的格子狀照片牆

搭配 NSCache 暫存網路圖片。

先將 UIBackgroundConfiguration 的 image 設為預設圖片,抓到圖後再將 image 設為抓到的圖片。

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
backgroundConfiguration.imageContentMode = .scaleAspectFill
backgroundConfiguration.image = UIImage(systemName: "photo")
cell.backgroundConfiguration = backgroundConfiguration

let feed = feeds[indexPath.item]
NetworkController.shared.fetchImage(url: feed.artworkUrl100) { [weak self] image in
DispatchQueue.main.async {
guard let self = self,
let image = image,
let item = collectionView.indexPath(for: cell)?.item,
feed.id == self.feeds[item].id else { return }

backgroundConfiguration.image = image
cell.backgroundConfiguration = backgroundConfiguration
}
}

return cell
}

網路圖片照片牆的範例連結。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com