UICollectionView — komponen UIKit yang kuat untuk iOS yang mengatur tampilan koleksi objek dalam bentuk grid, daftar, dan tata letak kustom. Berbeda dengan UITableView, UICollectionView tidak terbatas pada satu kolom: ia mendukung penempatan elemen secara arbitrer melalui UICollectionViewLayout. Menjelaskan esensi UICollectionView, cara kerja FlowLayout dan CompositionalLayout, serta cara membuat sel kustom di Swift. Di IT Sectr kami menggunakan UICollectionView untuk galeri, katalog, dan papan pengumuman. Untuk perbandingan dengan tabel, baca artikel tentang UITableView.
Poin utama
UICollectionView — adalah kelas dari framework UIKit, diperkenalkan di iOS 6 (2012). Kelas ini menampilkan koleksi sel (UICollectionViewCell) yang teratur dalam tata letak fleksibel yang dikelola oleh objek UICollectionViewLayout. UICollectionView menyelesaikan tugas yang sama dengan UITableView (daftar), tetapi dengan penempatan elemen secara arbitrer: grid, korsel, mozaik, kaskade.
Arsitektur UICollectionView dibangun di atas pola MVC: data — di DataSource, tata letak — di UICollectionViewLayout, perilaku — di UICollectionViewDelegate. Pemisahan ini memungkinkan penggantian tata letak tanpa mengubah kode data. Menurut Apple (WWDC 2025), 72% aplikasi iOS di 100 besar App Store menggunakan UICollectionView untuk menampilkan konten. CollectionView mendukung tata letak multi-kolom sejak iOS 6, dan sejak iOS 13 mendapatkan CompositionalLayout untuk bagian kompleks.
UICollectionView menggunakan kumpulan sel yang dapat digunakan kembali (reuse queue), mirip dengan UITableView. Metode dequeueReusableCell(withReuseIdentifier:for:) mengembalikan sel dari kumpulan atau membuat yang baru jika kumpulan kosong. Berbeda dengan UITableView, sel UICollectionViewCell memiliki contentView bawaan tempat semua subview harus ditambahkan — jangan menambahkannya langsung ke sel, karena contentView bertanggung jawab atas animasi pemilihan dan pengeditan yang benar.
UICollectionViewFlowLayout — tata letak standar yang berfungsi sejak iOS 6. Menempatkan elemen dalam baris (flow) dengan kemampuan mengatur ukuran sel, arah gulir, jarak minimum, dan bagian. UICollectionViewCompositionalLayout — tata letak modern (iOS 13+) yang mendeskripsikan tata letak melalui kombinasi blok: grup → bagian → tata letak, di mana setiap grup dapat horizontal, vertikal, atau kustom.
| Parameter | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Versi minimum | iOS 6 | iOS 13 |
| Tata letak kompleks | Hanya grid atau daftar sederhana | Bagian dengan tata letak berbeda, grup, korsel |
| Adaptabilitas | Melalui ukuran delegasi | NSCollectionLayoutDimension.fractionalWidth/Height |
| Kinerja | Merender semua elemen yang terlihat | Pembuatan bagian secara malas |
| Dukungan iPad | Memerlukan adaptasi manual | Grup adaptif melalui fractionalWidth |
| Kompleksitas penulisan | Rendah (5–10 baris) | Sedang (15–30 baris) |
// UICollectionView dasar dengan FlowLayout
class ViewController: UIViewController {
private var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumInteritemSpacing = 8
layout.minimumLineSpacing = 12
layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
layout.itemSize = CGSize(width: view.frame.width - 32, height: 100)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
collectionView.dataSource = self
view.addSubview(collectionView)
}
}
// DataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int { 20 }
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
as! CustomCell
cell.configure(with: "Item \(indexPath.row)")
return cell
}
}
Rekomendasi Apple (WWDC 2025): untuk proyek baru gunakan CompositionalLayout. Memberikan lebih banyak kemungkinan untuk tata letak adaptif, bekerja lebih baik dengan UIKit dan SwiftUI melalui UICollectionView.Representable, dan mendukung header bagian dengan ukuran otomatis. Biarkan FlowLayout untuk layar sederhana dengan grid sel identik dan versi minimum iOS 12 ke bawah.
UICollectionViewCell — kelas dasar untuk sel UICollectionView. Setiap sel berisi contentView (kontainer utama), backgroundView (latar belakang default), selectedBackgroundView (latar belakang saat dipilih). Sesuaikan tampilan sel di dalam contentView — jangan pernah menambahkan subview langsung ke UICollectionViewCell.
// Sel kustom dengan konfigurasi
class ProductCell: UICollectionViewCell {
private let imageView = UIImageView()
private let titleLabel = UILabel()
private let priceLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
contentView.addSubview(imageView)
contentView.addSubview(titleLabel)
contentView.addSubview(priceLabel)
// Auto Layout constraints
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 8),
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
priceLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
priceLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8)
])
}
func configure(with product: Product) {
imageView.image = UIImage(named: product.imageName)
titleLabel.text = product.title
priceLabel.text = product.price
}
}
UICollectionViewDelegate menyediakan metode untuk menangani pemilihan, penyorotan, menu konteks, dan pengurutan ulang. Berbeda dengan UITableView, UICollectionViewDelegate memungkinkan kontrol tidak hanya klik pada elemen, tetapi juga tekan lama (menu konteks melalui UIContextMenuInteraction). Untuk menangani klik, implementasikan collectionView(_:didSelectItemAt:) — dipanggil setelah animasi pemilihan selesai.
UICollectionViewDiffableDataSource — pengganti UICollectionViewDataSource yang aman tipe, diperkenalkan di iOS 13. Alih-alih mengelola sel secara manual melalui cellForItemAt, DiffableDataSource menggunakan NSDiffableDataSourceSnapshot — gambaran status data yang secara otomatis menghitung perbedaan antara status lama dan baru serta menganimasikan perubahan.
// DiffableDataSource dengan bagian
enum Section {
case featured, recommended, categories
}
class ModernDataSource: UIViewController {
private var dataSource: UICollectionViewDiffableDataSource<Section, Product>!
override func viewDidLoad() {
super.viewDidLoad()
configureDataSource()
applyInitialSnapshot()
}
private func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<ProductCell, Product> { cell, _, product in
cell.configure(with: product)
}
dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
collectionView, indexPath, product in
collectionView.dequeueConfiguredReusableCell(
using: cellRegistration, for: indexPath, item: product
)
}
}
private func applyInitialSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Product>()
snapshot.appendSections([.featured, .recommended, .categories])
snapshot.appendItems(featuredProducts, toSection: .featured)
snapshot.appendItems(recommendedProducts, toSection: .recommended)
snapshot.appendItems(categories, toSection: .categories)
dataSource.apply(snapshot, animatingDifferences: true)
}
// Pembaruan dengan animasi otomatis
func updateProducts(_ products: [Product]) {
var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers)
snapshot.appendItems(products)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
Keunggulan DiffableDataSource: (1) animasi otomatis — insert, delete, reload terjadi dengan animasi default; (2) keamanan tipe — bagian dan elemen diketik, kesalahan kompilasi alih-alih kesalahan runtime; (3) Snapshot — gambaran status dapat disimpan, dibatalkan, dibandingkan; (4) kinerja — DiffableDataSource menggunakan algoritma Hellir untuk menghitung set perubahan minimum. Apple (WWDC 2024) merekomendasikan DiffableDataSource untuk semua UICollectionView baru, dan DataSource lama hanya untuk iOS 12 dan yang lebih tua.
UICollectionViewLayout — kelas abstrak yang sepenuhnya mengontrol penempatan elemen koleksi. Jika FlowLayout dan CompositionalLayout tidak cocok (misalnya, tata letak melingkar, spiral, diagram), buat subkelas UICollectionViewLayout dan timpa metode persiapan tata letak.
// Tata letak melingkar kustom
class CircularLayout: UICollectionViewLayout {
private var attributesCache: [UICollectionViewLayoutAttributes] = []
private let radius: CGFloat = 120
override func prepare() {
super.prepare()
attributesCache.removeAll()
guard let collectionView else { return }
let count = collectionView.numberOfItems(inSection: 0)
let center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)
for i in 0..<count {
let indexPath = IndexPath(item: i, section: 0)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let angle = (CGFloat(i) / CGFloat(count)) * 2 * .pi
attributes.center = CGPoint(
x: center.x + radius * cos(angle),
y: center.y + radius * sin(angle)
)
attributes.size = CGSize(width: 60, height: 60)
attributesCache.append(attributes)
}
}
override var collectionViewContentSize: CGSize {
collectionView?.bounds.size ?? .zero
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
attributesCache.filter { $0.frame.intersects(rect) }
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
attributesCache[indexPath.item]
}
}
Kinerja: UICollectionViewLayout kustom harus menyimpan cache atribut di prepare() dan mengembalikannya dalam O(1) di layoutAttributesForItem(at:). Untuk koleksi dengan 1000+ elemen, gunakan pembaruan cache inkremental melalui shouldInvalidateLayout(forBoundsChange:) — ini memungkinkan untuk tidak menghitung ulang seluruh tata letak saat menggulir. Apple (WWDC 2023) merekomendasikan CompositionalLayout daripada tata letak kustom: mencakup 95% skenario tanpa perlu menulis tata letak dari awal.
UITableView dan UICollectionView — dua komponen utama UIKit untuk menampilkan daftar. UITableView lebih sederhana dan ditujukan untuk daftar satu kolom. UICollectionView lebih fleksibel dan mendukung tata letak apa pun. Pilihan di antara keduanya tergantung pada struktur visual data dan kustomisasi yang diperlukan.
| Parameter | UITableView | UICollectionView |
|---|---|---|
| Tata letak | Hanya daftar vertikal | Daftar, grid, kaskade, korsel, kustom |
| Kompleksitas | Rendah (DataSource sederhana) | Sedang (Layout + DataSource + Delegate) |
| Sel default | 4 gaya (basic, subtitle, value1, value2) | Hanya kosong (kustom melalui contentView) |
| Mode edit | Bawaan (delete, move, insert) | Memerlukan implementasi manual |
| Header/Footer | Bawaan (viewForHeaderInSection) | Melalui tampilan tambahan |
| Kinerja | Tinggi untuk daftar | Tinggi untuk tata letak apa pun |
| iOS 6+ | Tersedia sejak iOS 2 | Tersedia sejak iOS 6 |
Aturan praktis: gunakan UITableView jika layar adalah daftar baris sederhana (pengaturan, kontak, menu). Gunakan UICollectionView jika layar memerlukan grid, gulir horizontal, korsel, ukuran sel berbeda, atau pengelompokan kompleks. Sejak iOS 14, UITableView juga mendapatkan dukungan CompositionalLayout melalui UICollectionLayoutListConfiguration, yang memungkinkan menggabungkan keunggulan kedua komponen.
Pertanyaan yang sering diajukan
Atur scrollDirection = .horizontal di UICollectionViewFlowLayout. Untuk CompositionalLayout gunakan NSCollectionLayoutSize dengan fractionalWidth untuk lebar grup adaptif. Gulir horizontal baik untuk korsel, galeri, dan kategori. Untuk gulir halaman, atur collectionView.isPagingEnabled = true atau gunakan UICollectionViewFlowLayout dengan itemSize = bounds.width.
Periksa: (1) sel telah didaftarkan melalui register(_:forCellWithReuseIdentifier:), (2) DataSource mengembalikan jumlah elemen > 0, (3) sel didequeue dengan reuseIdentifier yang benar, (4) itemSize tidak nol (untuk FlowLayout). Kesalahan umum — itemSize = CGSize.zero, yang menyebabkan sel memiliki tinggi nol. Atur itemSize secara eksplisit atau implementasikan delegasi collectionView(_:layout:sizeForItemAt:).
Untuk FlowLayout: layout.minimumInteritemSpacing (antar elemen dalam satu baris) dan layout.minimumLineSpacing (antar baris). Untuk CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing dan contentInsets pada grup/bagian. sectionInset mengatur jarak eksternal bagian. Gunakan sectionInsetReference = .fromContentInset untuk memperhitungkan Safe Area dengan benar.
Gunakan performBatchUpdates untuk animasi grup: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Untuk pembaruan yang aman dan beranimasi, gunakan UICollectionViewDiffableDataSource: panggil dataSource.apply(snapshot, animatingDifferences: true) — semua perubahan akan dihitung dan diterapkan secara otomatis tanpa insert/delete manual.
Di SwiftUI, padanan UICollectionView adalah LazyVGrid dan LazyHGrid (iOS 14+). SwiftUI Grid lebih sederhana dalam penulisan (sintaks deklaratif), tetapi kalah dari UICollectionView dalam kinerja untuk 500+ elemen dan kustomisasi tata letak. Untuk koleksi kompleks, gunakan UICollectionView melalui UIViewRepresentable. Dalam proyek IT Sectr kami memilih UICollectionView untuk katalog dan galeri, SwiftUI Grid untuk layar sederhana.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga