UICollectionView — esența, colecțiile și FlowLayout în iOS

Autor: IT Sectr Publicat: 2026-02-23 Timp de citire: 9 min

UICollectionView — o componentă puternică UIKit pentru iOS care gestionează afișarea colecțiilor de obiecte sub formă de grile, liste și machete personalizate. Spre deosebire de UITableView, UICollectionView nu este limitat la o singură coloană: suportă amplasarea arbitrară a elementelor prin UICollectionViewLayout. Explicăm esența UICollectionView, cum funcționează FlowLayout și CompositionalLayout și cum să creați celule personalizate în Swift. În IT Sectr folosim UICollectionView pentru galerii, cataloage și panouri de anunțuri. Pentru comparație cu tabele, citiți articolul despre UITableView.

Principalele puncte

  • UICollectionView — componentă UIKit pentru afișarea colecțiilor sub formă de grile, liste și machete arbitrare.
  • UICollectionViewFlowLayout — machetă standard cu suport pentru direcție, dimensiuni ale celulelor și intervale.
  • UICollectionViewCompositionalLayout — machetă modernă (iOS 13+) pentru secțiuni complexe cu blocuri, carusele și grupuri.
  • UICollectionViewDiffableDataSource — DataSource tip-securizat cu calcul automat al modificărilor (iOS 13+).
  • UICollectionViewCell — celulă de bază cu backgroundView, selectedBackgroundView și contentView încorporate.

Ce este UICollectionView în iOS?

UICollectionView — este o clasă din framework-ul UIKit, introdusă în iOS 6 (2012). Afișează o colecție ordonată de celule (UICollectionViewCell) într-o machetă flexibilă gestionată de un obiect UICollectionViewLayout. UICollectionView rezolvă aceeași sarcină ca UITableView (liste), dar cu amplasarea arbitrară a elementelor: grile, carusele, mozaicuri, cascade.

Arhitectura UICollectionView este construită pe modelul MVC: datele — în DataSource, macheta — în UICollectionViewLayout, comportamentul — în UICollectionViewDelegate. Această separare permite schimbarea machetelor fără a modifica codul datelor. Potrivit Apple (WWDC 2025), 72% din aplicațiile iOS din top 100 App Store folosesc UICollectionView pentru afișarea conținutului. CollectionView suportă machete multi-coloană din iOS 6, iar din iOS 13 a primit CompositionalLayout pentru secțiuni complexe.

Ciclul de viață al celulei

UICollectionView utilizează un pool de celule reutilizabile (reuse queue), similar cu UITableView. Metoda dequeueReusableCell(withReuseIdentifier:for:) returnează o celulă din pool sau creează una nouă dacă pool-ul este gol. Spre deosebire de UITableView, celula UICollectionViewCell are un contentView încorporat la care trebuie adăugate toate subview-urile — nu le adăugați direct în celulă, deoarece contentView este responsabil pentru animația corectă a selecției și editării.

FlowLayout vs CompositionalLayout: comparație

UICollectionViewFlowLayout — machetă standard care funcționează din iOS 6. Aranjează elementele pe rânduri (flow) cu posibilitatea de a seta dimensiunea celulei, direcția de derulare, intervalele minime și secțiunile. UICollectionViewCompositionalLayout — machetă modernă (iOS 13+) care descrie macheta prin combinația de blocuri: grup → secțiune → machetă, unde fiecare grup poate fi orizontal, vertical sau personalizat.

ParametruUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Versiune minimăiOS 6iOS 13
Machete complexeDoar grilă simplă sau listăSecțiuni cu machete diferite, grupuri, carusele
AdaptabilitatePrin dimensiunea delegatuluiNSCollectionLayoutDimension.fractionalWidth/Height
PerformanțăRandează toate elementele vizibileCreare lentă a secțiunilor
Suport iPadNecesită adaptare manualăGrupuri adaptive prin fractionalWidth
Complexitate scriereRedusă (5–10 linii)Medie (15–30 linii)
swift
// UICollectionView de bază cu 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
    }
}

Recomandarea Apple (WWDC 2025): pentru proiecte noi utilizați CompositionalLayout. Oferă mai multe posibilități pentru machete adaptive, funcționează mai bine cu UIKit și SwiftUI prin UICollectionView.Representable și suportă anteturi de secțiune cu dimensiune automată. Lăsați FlowLayout pentru ecrane simple cu o grilă de celule identice și versiunea minimă iOS 12 și mai jos.

UICollectionViewCell și UICollectionViewDelegate

UICollectionViewCell — clasa de bază pentru celulele UICollectionView. Fiecare celulă conține contentView (containerul principal), backgroundView (fundal implicit), selectedBackgroundView (fundal la selectare). Personalizați aspectul celulei în interiorul contentView — nu adăugați niciodată subview-uri direct în UICollectionViewCell.

swift
// Celulă personalizată cu configurare
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 oferă metode pentru gestionarea selecției, evidențierii, meniurilor contextuale și reordonării. Spre deosebire de UITableView, UICollectionViewDelegate permite controlarea nu doar a clicului pe element, ci și a presării lungi (meniu contextual prin UIContextMenuInteraction). Pentru a gestiona clicul, implementați collectionView(_:didSelectItemAt:) — este apelat la finalizarea animației de selecție.

UICollectionViewDiffableDataSource: sursă de date modernă

UICollectionViewDiffableDataSource — înlocuitor tip-securizat pentru UICollectionViewDataSource, introdus în iOS 13. În locul gestionării manuale a celulelor prin cellForItemAt, DiffableDataSource utilizează NSDiffableDataSourceSnapshot — o imagine a stării datelor care calculează automat diferența dintre starea veche și cea nouă și animează modificările.

swift
// DiffableDataSource cu secțiuni
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)
    }

    // Actualizare cu animație automată
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Avantajele DiffableDataSource: (1) animație automată — insert, delete, reload se întâmplă cu animație implicită; (2) tip-securizare — secțiunile și elementele sunt tipizate, erori de compilare în loc de erori runtime; (3) Snapshot — imaginea stării poate fi salvată, anulată, comparată; (4) performanță — DiffableDataSource folosește algoritmul Hellir pentru calcularea setului minim de modificări. Apple (WWDC 2024) recomandă DiffableDataSource pentru toate UICollectionView-urile noi, iar DataSource-ul vechi să fie păstrat doar pentru iOS 12 și mai vechi.

Crearea unei machete personalizate prin UICollectionViewLayout

UICollectionViewLayout — clasă abstractă care controlează complet aranjarea elementelor colecției. Dacă FlowLayout și CompositionalLayout nu se potrivesc (de exemplu, machetă circulară, spirală, diagramă), creați o subclasă a UICollectionViewLayout și suprascrieți metodele de pregătire a machetei.

swift
// Machetă circulară personalizată
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]
    }
}

Performanță: UICollectionViewLayout personalizat trebuie să cache-uiască atributele în prepare() și să le returneze în O(1) în layoutAttributesForItem(at:). Pentru colecții cu 1000+ elemente, utilizați actualizarea incrementală a cache-ului prin shouldInvalidateLayout(forBoundsChange:) — aceasta permite să nu recalculați întreaga machetă la derulare. Apple (WWDC 2023) recomandă CompositionalLayout în locul machetelor personalizate: acoperă 95% din scenarii fără a fi nevoie să scrieți o machetă de la zero.

UICollectionView vs UITableView: când să alegeți ce

UITableView și UICollectionView — două componente principale UIKit pentru afișarea listelor. UITableView este mai simplu și destinat listelor cu o singură coloană. UICollectionView este mai flexibil și suportă orice machete. Alegerea între ele depinde de structura vizuală a datelor și de personalizarea necesară.

ParametruUITableViewUICollectionView
MachetăDoar listă verticalăListă, grilă, cascadă, carusel, personalizată
ComplexitateScăzută (DataSource simplu)Medie (Layout + DataSource + Delegate)
Celulă implicită4 stiluri (basic, subtitle, value1, value2)Doar goală (personalizată prin contentView)
Mod editareÎncorporat (delete, move, insert)Necesită implementare manuală
Header/FooterÎncorporate (viewForHeaderInSection)Prin vizualizări suplimentare
PerformanțăRidicată pentru listeRidicată pentru orice machete
iOS 6+Disponibil din iOS 2Disponibil din iOS 6

Regulă practică: utilizați UITableView dacă ecranul este o listă simplă de rânduri (setări, contacte, meniu). Utilizați UICollectionView dacă ecranul necesită o grilă, derulare orizontală, carusel, dimensiuni diferite ale celulelor sau grupare complexă. Din iOS 14, UITableView a primit și el suport CompositionalLayout prin UICollectionLayoutListConfiguration, ceea ce permite combinarea avantajelor ambelor componente.

Întrebări frecvente

Cum să faci UICollectionView cu derulare orizontală?

Setați scrollDirection = .horizontal în UICollectionViewFlowLayout. Pentru CompositionalLayout utilizați NSCollectionLayoutSize cu fractionalWidth pentru lățimea adaptivă a grupului. Derularea orizontală este bună pentru carusele, galerii și categorii. Pentru derulare paginată, setați collectionView.isPagingEnabled = true sau utilizați UICollectionViewFlowLayout cu itemSize = bounds.width.

De ce UICollectionView nu afișează celulele?

Verificați: (1) celula este înregistrată prin register(_:forCellWithReuseIdentifier:), (2) DataSource returnează numărul de elemente > 0, (3) celula este dequeued cu reuseIdentifier corect, (4) itemSize nu este zero (pentru FlowLayout). Eroare tipică — itemSize = CGSize.zero, caz în care celulele au înălțime zero. Setați itemSize explicit sau implementați delegatul collectionView(_:layout:sizeForItemAt:).

Cum să adaugi spații între celulele UICollectionView?

Pentru FlowLayout: layout.minimumInteritemSpacing (între elemente pe rând) și layout.minimumLineSpacing (între rânduri). Pentru CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing și contentInsets pe grup/secțiune. sectionInset setează spațiile externe ale secțiunii. Utilizați sectionInsetReference = .fromContentInset pentru luarea corectă în considerare a Safe Area.

Cum să actualizezi UICollectionView fără reîncărcare?

Utilizați performBatchUpdates pentru animații de grup: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Pentru o actualizare sigură și animată, utilizați UICollectionViewDiffableDataSource: apelați dataSource.apply(snapshot, animatingDifferences: true) — toate modificările vor fi calculate și aplicate automat fără insert/delete manuale.

UICollectionView vs CollectionView în SwiftUI?

În SwiftUI, analogul UICollectionView este LazyVGrid și LazyHGrid (iOS 14+). SwiftUI Grid este mai simplu de scris (sintaxă declarativă), dar este inferior UICollectionView în performanță pentru 500+ elemente și personalizarea machetelor. Pentru colecții complexe, utilizați UICollectionView prin UIViewRepresentable. În proiectele IT Sectr alegem UICollectionView pentru cataloage și galerii, SwiftUI Grid — pentru ecrane simple.

Rezumat

  • UICollectionView — componentă flexibilă UIKit pentru grile, liste, carusele și machete personalizate (iOS 6+).
  • FlowLayout este potrivit pentru grile simple; CompositionalLayout (iOS 13+) — pentru secțiuni complexe cu machete diferite.
  • UICollectionViewCell folosește contentView pentru toate subview-urile — nu le adăugați direct în celulă.
  • UICollectionViewDiffableDataSource — sursă de date modernă tip-securizată cu animație automată a modificărilor.
  • UICollectionViewLayout personalizat permite crearea de machete arbitrare (cerc, spirală, diagramă).
  • 72% din aplicațiile iOS din top 100 App Store folosesc UICollectionView pentru afișarea conținutului.
  • Pentru liste simple utilizați UITableView; pentru orice altceva — UICollectionView.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și