UICollectionView แสดงข้อมูลแบบ Grid/Custom Layout ใช้ DataSource, Delegate, FlowLayout
Setup แบบโปรแกรม
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
layout.scrollDirection = .vertical
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .systemBackground
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Header")
view.addSubview(collectionView)
}
// DataSource
func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = .systemBlue
cell.layer.cornerRadius = 8
// Add Label
if cell.contentView.subviews.isEmpty {
let label = UILabel(frame: cell.contentView.bounds)
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.textAlignment = .center
label.textColor = .white
label.font = .boldSystemFont(ofSize: 16)
label.tag = 100
cell.contentView.addSubview(label)
}
(cell.contentView.viewWithTag(100) as? UILabel)?.text = "\(indexPath.item)"
return cell
}
// Delegate - Selection
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Selected: \(indexPath.item)")
}
// FlowLayout Delegate - Dynamic Size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (collectionView.bounds.width - 30) / 2 // 2 columns
return CGSize(width: width, height: width)
}
}
Compositional Layout (iOS 13+ แนะนำ)
func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(0.5))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
return UICollectionViewCompositionalLayout(section: section)
}
// ใช้งาน
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
Diffable Data Source (iOS 13+)
typealias DataSource = UICollectionViewDiffableDataSource
typealias Snapshot = NSDiffableDataSourceSnapshot
enum Section { case main }
struct Item: Hashable { let id = UUID(); let title: String }
lazy var dataSource = DataSource(collectionView: collectionView) { cv, indexPath, item in
let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = .systemBlue
(cell.contentView.viewWithTag(100) as? UILabel)?.text = item.title
return cell
}
func applySnapshot(items: [Item]) {
var snapshot = Snapshot()
snapshot.appendSections([.main])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
Cell Registration (iOS 14+)
let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in
var config = UIListContentConfiguration.cell()
config.text = item.title
config.textProperties.color = .white
config.textProperties.font = .boldSystemFont(ofSize: 16)
cell.contentConfiguration = config
cell.backgroundConfiguration = .clear()
cell.layer.cornerRadius = 8
}
dataSource = DataSource(collectionView: collectionView) { cv, indexPath, item in
cv.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
}