利用 NSCollectionLayoutDecorationItem 設定 collection view section 的 background
從 iOS 13 開始,collection view 利用 UICollectionViewCompositionalLayout 排版時,可以搭配 NSCollectionLayoutDecorationItem 設定 section 的背景,接下來就讓我們試試幾種不同的例子吧。
- 每個 section 搭配不同的背景顏色
- 設定 section 背景的 inset & 圓角
- 客製 section 背景,比方設定背景圖片
開發前的準備
storyboard 畫面如下。
CollectionViewCell 的 outlet imageView 連到 cell 上的 image view。
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
}
使用 UICollectionViewCompositionalLayout 排版,分成 2 個 section。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.collectionViewLayout = generateLayout()
}
func generateLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { sectionIndex, environment in
let space: Double = 10
let itemCountInGroup = sectionIndex == 0 ? 1 : 2
let ratio = 1 / Double(itemCountInGroup)
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(ratio), heightDimension: .fractionalWidth(ratio))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(100))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: space, bottom: 0, trailing: space)
section.orthogonalScrollingBehavior = .continuous
return section
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return 3
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(CollectionViewCell.self)", for: indexPath) as! CollectionViewCell
if indexPath.section == 0 {
cell.imageView.image = UIImage(named: "book\(indexPath.item + 1)")
} else {
cell.imageView.image = UIImage(named: "Radio Drama \(indexPath.item + 1)")
}
return cell
}
}
結果
每個 section 搭配不同的背景顏色
- 新增繼承 UICollectionReusableView 的型別
想成為 section 的背景,它的型別必須繼承 UICollectionReusableView,以下我們定義紅色背景 RedBackgroundDecorationView & 黃色背景 YellowBackgroundDecorationView。
class RedBackgroundDecorationView: UICollectionReusableView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class YellowBackgroundDecorationView: UICollectionReusableView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .yellow
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 註冊要成為 section 背景的 view
呼叫 register(_:forDecorationViewOfKind:)。
override func viewDidLoad() {
super.viewDidLoad()
collectionView.collectionViewLayout = generateLayout()
collectionView.collectionViewLayout.register(RedBackgroundDecorationView.self, forDecorationViewOfKind: "\(RedBackgroundDecorationView.self)")
collectionView.collectionViewLayout.register(YellowBackgroundDecorationView.self, forDecorationViewOfKind: "\(YellowBackgroundDecorationView.self)")
}
- 設定 section 搭配的背景
設定 section 的 decorationItems,利用 NSCollectionLayoutDecorationItem 的 background(elementKind:) 指定搭配的背景,比方 NSCollectionLayoutDecorationItem.background(elementKind: "\(RedBackgroundDecorationView.self)")
表示搭配的背景是紅色背景 RedBackgroundDecorationView。
func generateLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { sectionIndex, environment in
let space: Double = 10
let itemCountInGroup = sectionIndex == 0 ? 1 : 2
let ratio = 1 / Double(itemCountInGroup)
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(ratio), heightDimension: .fractionalWidth(ratio))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(100))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: space, bottom: 0, trailing: space)
section.orthogonalScrollingBehavior = .continuous
let redBackgroundDecorationView = NSCollectionLayoutDecorationItem.background(elementKind: "\(RedBackgroundDecorationView.self)")
let yellowBackgroundDecorationView = NSCollectionLayoutDecorationItem.background(elementKind: "\(YellowBackgroundDecorationView.self)")
section.decorationItems = sectionIndex == 0 ? [redBackgroundDecorationView] :[yellowBackgroundDecorationView]
return section
}
}
結果
設定 section 背景的 inset & 圓角
在 RedBackgroundDecorationView 設定實現圓角效果的 cornerRadius。
class RedBackgroundDecorationView: UICollectionReusableView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
layer.cornerRadius = 10
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
view controller 的程式。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.collectionViewLayout = generateLayout() collectionView.collectionViewLayout.register(RedBackgroundDecorationView.self, forDecorationViewOfKind: "\(RedBackgroundDecorationView.self)")
}
func generateLayout() -> UICollectionViewLayout {
let space: Double = 10
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(150))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 3)
group.interItemSpacing = .fixed(space)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
let redBackgroundDecorationView = NSCollectionLayoutDecorationItem.background(elementKind: "\(RedBackgroundDecorationView.self)")
redBackgroundDecorationView.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
section.decorationItems = [redBackgroundDecorationView]
return UICollectionViewCompositionalLayout(section: section)
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(CollectionViewCell.self)", for: indexPath) as! CollectionViewCell
cell.imageView.image = UIImage(named: "Radio Drama \(indexPath.item + 1)")
return cell
}
}
說明
設定 redBackgroundDecorationView.contentInsets,讓 section 的背景跟上下左右有間距,實現類似卡片的背景效果。
結果
客製 section 背景,比方設定背景圖片
新增 xib 設計 section 的背景,在 UICollectionReusableView 裡加入 image view。
設定圓角弧度。
view controller 的程式。
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.collectionViewLayout = generateLayout() collectionView.collectionViewLayout.register(UINib(nibName: "\(ImageBackgroundDecorationView.self)", bundle: nil), forDecorationViewOfKind: "\(ImageBackgroundDecorationView.self)")
}
func generateLayout() -> UICollectionViewLayout {
let space: Double = 10
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(150))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 3)
group.interItemSpacing = .fixed(space)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
let imageBackgroundDecorationView = NSCollectionLayoutDecorationItem.background(elementKind: "\(ImageBackgroundDecorationView.self)")
imageBackgroundDecorationView.contentInsets = NSDirectionalEdgeInsets(top: space, leading: space, bottom: space, trailing: space)
section.decorationItems = [imageBackgroundDecorationView]
return UICollectionViewCompositionalLayout(section: section)
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(CollectionViewCell.self)", for: indexPath) as! CollectionViewCell
cell.imageView.image = UIImage(named: "Radio Drama \(indexPath.item + 1)")
return cell
}
}
結果