UICollectionView — isang makapangyarihang bahagi ng UIKit para sa iOS na namamahala sa pagpapakita ng mga koleksyon ng bagay sa anyo ng mga grid, listahan, at custom na layout. Hindi tulad ng UITableView, ang UICollectionView ay hindi limitado sa isang kolum: sinusuportahan nito ang arbitraryong paglalagay ng mga elemento sa pamamagitan ng UICollectionViewLayout. Ipinapaliwanag ang esensya ng UICollectionView, kung paano gumagana ang FlowLayout at CompositionalLayout, at kung paano gumawa ng custom na mga cell sa Swift. Sa IT Sectr ginagamit namin ang UICollectionView para sa mga gallery, katalogo, at bulletin board. Para sa paghahambing sa mga talahanayan, basahin ang artikulo tungkol sa UITableView.
Mga pangunahing punto
UICollectionView — ay isang klase mula sa UIKit framework, ipinakilala sa iOS 6 (2012). Ipinapakita nito ang isang maayos na koleksyon ng mga cell (UICollectionViewCell) sa isang nababaluktot na layout na pinamamahalaan ng isang UICollectionViewLayout object. Nilulutas ng UICollectionView ang parehong gawain tulad ng UITableView (mga listahan), ngunit may arbitraryong paglalagay ng mga elemento: mga grid, carousel, mosaic, cascade.
Arkitektura ng UICollectionView ay binuo sa MVC pattern: data — sa DataSource, layout — sa UICollectionViewLayout, pag-uugali — sa UICollectionViewDelegate. Ang paghihiwalay na ito ay nagbibigay-daan sa pagpapalit ng mga layout nang hindi binabago ang code ng data. Ayon sa Apple (WWDC 2025), 72% ng mga iOS app sa top 100 ng App Store ay gumagamit ng UICollectionView para sa pagpapakita ng nilalaman. Sinusuportahan ng CollectionView ang multi-kolum na layout mula iOS 6, at mula iOS 13 ay nakatanggap ito ng CompositionalLayout para sa kumplikadong mga seksyon.
Ang UICollectionView ay gumagamit ng pool ng mga reusable na cell (reuse queue), katulad ng UITableView. Ang pamamaraang dequeueReusableCell(withReuseIdentifier:for:) ay nagbabalik ng cell mula sa pool o gumagawa ng bago kung walang laman ang pool. Hindi tulad ng UITableView, ang UICollectionViewCell ay may built-in na contentView kung saan dapat idagdag ang lahat ng subview — huwag idagdag ang mga ito nang direkta sa cell, dahil ang contentView ay responsable para sa tamang animasyon ng pagpili at pag-edit.
UICollectionViewFlowLayout — karaniwang layout na gumagana mula iOS 6. Inaayos nito ang mga elemento sa mga hilera (flow) na may kakayahang itakda ang laki ng cell, direksyon ng pag-scroll, minimum na pagitan, at mga seksyon. UICollectionViewCompositionalLayout — modernong layout (iOS 13+) na naglalarawan ng layout sa pamamagitan ng kumbinasyon ng mga bloke: grupo → seksyon → layout, kung saan ang bawat grupo ay maaaring pahalang, patayo, o custom.
| Parameter | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Minimum na bersyon | iOS 6 | iOS 13 |
| Komplikadong layout | Simpleng grid o listahan lamang | Mga seksyon na may iba't ibang layout, grupo, carousel |
| Adaptabilidad | Sa pamamagitan ng laki ng delegado | NSCollectionLayoutDimension.fractionalWidth/Height |
| Pagganap | Ni-render ang lahat ng nakikitang elemento | Tamad na paggawa ng mga seksyon |
| Suporta sa iPad | Nangangailangan ng manu-manong adaptasyon | Adaptive na grupo sa pamamagitan ng fractionalWidth |
| Kahirapan sa pagsulat | Mababa (5–10 linya) | Katamtaman (15–30 linya) |
// Batayang UICollectionView na may 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
}
}
Rekomendasyon ng Apple (WWDC 2025): para sa mga bagong proyekto gamitin ang CompositionalLayout. Nagbibigay ito ng mas maraming posibilidad para sa adaptive na layout, mas mahusay na gumagana sa UIKit at SwiftUI sa pamamagitan ng UICollectionView.Representable, at sinusuportahan ang mga header ng seksyon na may awtomatikong laki. Iwanan ang FlowLayout para sa mga simpleng screen na may grid ng magkaparehong cell at minimum na bersyon na iOS 12 pababa.
UICollectionViewCell — pangunahing klase para sa mga cell ng UICollectionView. Ang bawat cell ay naglalaman ng contentView (pangunahing lalagyan), backgroundView (default na background), selectedBackgroundView (background kapag pinili). Ayusin ang hitsura ng cell sa loob ng contentView — huwag kailanman magdagdag ng subview nang direkta sa UICollectionViewCell.
// Custom na cell na may configuration
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 ay nagbibigay ng mga pamamaraan para sa paghawak ng pagpili, pag-highlight, mga menu ng konteksto, at pag-reorder. Hindi tulad ng UITableView, ang UICollectionViewDelegate ay nagbibigay-daan sa pagkontrol hindi lamang ng pag-click sa isang elemento, kundi pati na rin ng mahabang pagpindot (menu ng konteksto sa pamamagitan ng UIContextMenuInteraction). Upang mahawakan ang pag-click, ipatupad ang collectionView(_:didSelectItemAt:) — ito ay tinatawag pagkatapos makumpleto ang animasyon ng pagpili.
UICollectionViewDiffableDataSource — type-safe na kapalit ng UICollectionViewDataSource, ipinakilala sa iOS 13. Sa halip na manu-manong pamahalaan ang mga cell sa pamamagitan ng cellForItemAt, ang DiffableDataSource ay gumagamit ng NSDiffableDataSourceSnapshot — isang snapshot ng estado ng data na awtomatikong kinakalkula ang pagkakaiba sa pagitan ng luma at bagong estado at nag-a-animate ng mga pagbabago.
// DiffableDataSource na may mga seksyon
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)
}
// Update na may awtomatikong animasyon
func updateProducts(_ products: [Product]) {
var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers)
snapshot.appendItems(products)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
Mga bentahe ng DiffableDataSource: (1) awtomatikong animasyon — insert, delete, reload ay nangyayari na may default na animasyon; (2) type-safety — ang mga seksyon at elemento ay naka-type, mga error sa compilation sa halip na runtime error; (3) Snapshot — ang snapshot ng estado ay maaaring i-save, i-undo, ihambing; (4) pagganap — gumagamit ang DiffableDataSource ng Hellir algorithm para sa pagkalkula ng minimum na set ng mga pagbabago. Inirerekomenda ng Apple (WWDC 2024) ang DiffableDataSource para sa lahat ng bagong UICollectionView, at iwanan ang lumang DataSource para lamang sa iOS 12 at mas luma.
UICollectionViewLayout — abstract na klase na ganap na kumokontrol sa paglalagay ng mga elemento ng koleksyon. Kung hindi angkop ang FlowLayout at CompositionalLayout (halimbawa, pabilog na layout, spiral, diagram), gumawa ng subclass ng UICollectionViewLayout at i-override ang mga pamamaraan ng paghahanda ng layout.
// Custom na pabilog na layout
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]
}
}
Pagganap: ang custom na UICollectionViewLayout ay dapat mag-cache ng mga attribute sa prepare() at ibalik ang mga ito sa O(1) sa layoutAttributesForItem(at:). Para sa mga koleksyon na may 1000+ elemento, gumamit ng incremental na pag-update ng cache sa pamamagitan ng shouldInvalidateLayout(forBoundsChange:) — pinapayagan nito na huwag muling kalkulahin ang buong layout habang nag-scroll. Inirerekomenda ng Apple (WWDC 2023) ang CompositionalLayout sa halip na mga custom na layout: sakop nito ang 95% ng mga sitwasyon nang hindi kailangang sumulat ng layout mula sa simula.
UITableView at UICollectionView — dalawang pangunahing bahagi ng UIKit para sa pagpapakita ng mga listahan. Ang UITableView ay mas simple at inilaan para sa mga listahan ng isang kolum. Ang UICollectionView ay mas nababaluktot at sumusuporta sa anumang layout. Ang pagpili sa pagitan ng mga ito ay depende sa visual na istraktura ng data at sa kinakailangang pagpapasadya.
| Parameter | UITableView | UICollectionView |
|---|---|---|
| Layout | Tanging patayong listahan | Listahan, grid, cascade, carousel, custom |
| Kompleksidad | Mababa (simpleng DataSource) | Katamtaman (Layout + DataSource + Delegate) |
| Default na cell | 4 na estilo (basic, subtitle, value1, value2) | Walang laman lamang (custom sa pamamagitan ng contentView) |
| Mode ng pag-edit | Built-in (delete, move, insert) | Nangangailangan ng manu-manong pagpapatupad |
| Header/Footer | Built-in (viewForHeaderInSection) | Sa pamamagitan ng mga karagdagang view |
| Pagganap | Mataas para sa mga listahan | Mataas para sa anumang layout |
| iOS 6+ | Magagamit mula iOS 2 | Magagamit mula iOS 6 |
Praktikal na tuntunin: gamitin ang UITableView kung ang screen ay isang simpleng listahan ng mga hilera (mga setting, contact, menu). Gamitin ang UICollectionView kung ang screen ay nangangailangan ng grid, pahalang na pag-scroll, carousel, iba't ibang laki ng cell o kumplikadong pagpapangkat. Mula iOS 14, nakatanggap din ang UITableView ng suporta sa CompositionalLayout sa pamamagitan ng UICollectionLayoutListConfiguration, na nagbibigay-daan sa pagsasama ng mga bentahe ng parehong bahagi.
Mga madalas itanong
Itakda ang scrollDirection = .horizontal sa UICollectionViewFlowLayout. Para sa CompositionalLayout gamitin ang NSCollectionLayoutSize na may fractionalWidth para sa adaptive na lapad ng grupo. Ang pahalang na pag-scroll ay mainam para sa mga carousel, gallery, at kategorya. Para sa pahina-sa-pahinang pag-scroll, itakda ang collectionView.isPagingEnabled = true o gamitin ang UICollectionViewFlowLayout na may itemSize = bounds.width.
Suriin: (1) ang cell ay naka-rehistro sa pamamagitan ng register(_:forCellWithReuseIdentifier:), (2) ang DataSource ay nagbabalik ng bilang ng mga elemento > 0, (3) ang cell ay naka-dequeue na may tamang reuseIdentifier, (4) ang itemSize ay hindi zero (para sa FlowLayout). Karaniwang error — itemSize = CGSize.zero, kung saan ang mga cell ay may zero na taas. Itakda ang itemSize nang hayagan o ipatupad ang delegado collectionView(_:layout:sizeForItemAt:).
Para sa FlowLayout: layout.minimumInteritemSpacing (sa pagitan ng mga elemento sa isang hilera) at layout.minimumLineSpacing (sa pagitan ng mga hilera). Para sa CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing at contentInsets sa grupo/seksyon. Tinutukoy ng sectionInset ang mga panlabas na pagitan ng seksyon. Gamitin ang sectionInsetReference = .fromContentInset para sa tamang pagsasaalang-alang ng Safe Area.
Gamitin ang performBatchUpdates para sa mga animasyon ng grupo: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Para sa ligtas at na-animate na pag-update, gamitin ang UICollectionViewDiffableDataSource: tawagan ang dataSource.apply(snapshot, animatingDifferences: true) — lahat ng pagbabago ay awtomatikong kakalkulahin at ilalapat nang walang manu-manong insert/delete.
Sa SwiftUI, ang katumbas ng UICollectionView ay LazyVGrid at LazyHGrid (iOS 14+). Ang SwiftUI Grid ay mas simple sa pagsulat (declarative syntax), ngunit mas mababa sa UICollectionView sa pagganap para sa 500+ elemento at pagpapasadya ng layout. Para sa kumplikadong mga koleksyon, gamitin ang UICollectionView sa pamamagitan ng UIViewRepresentable. Sa mga proyekto ng IT Sectr pinipili namin ang UICollectionView para sa mga katalogo at gallery, SwiftUI Grid para sa mga simpleng screen.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din