UICollectionView è un potente componente UIKit per iOS che gestisce la visualizzazione di collezioni di oggetti sotto forma di griglie, elenchi e layout personalizzati. A differenza di UITableView, UICollectionView non è limitato a una singola colonna: supporta il posizionamento arbitrario degli elementi tramite UICollectionViewLayout. Spieghiamo l'essenza di UICollectionView, come funzionano FlowLayout e CompositionalLayout e come creare celle personalizzate in Swift. In IT Sectr, usiamo UICollectionView per gallerie, cataloghi e bacheche. Per il confronto con le tabelle, leggi l'articolo su UITableView.
Punti chiave
UICollectionView è una classe del framework UIKit introdotta in iOS 6 (2012). Visualizza una collezione ordinata di celle (UICollectionViewCell) in un layout flessibile gestito da un oggetto UICollectionViewLayout. UICollectionView risolve lo stesso problema di UITableView (elenchi) ma con posizionamento arbitrario degli elementi: griglie, caroselli, mosaici, cascate.
Architettura UICollectionView è costruita sul pattern MVC: i dati nel DataSource, il layout in UICollectionViewLayout, il comportamento in UICollectionViewDelegate. Questa separazione consente di sostituire i layout senza modificare il codice dei dati. Secondo Apple (WWDC 2025), il 72% delle app iOS della top 100 dell'App Store utilizza UICollectionView per visualizzare contenuti. CollectionView supporta layout multi-colonna da iOS 6 e da iOS 13 ha ricevuto CompositionalLayout per sezioni complesse.
UICollectionView utilizza un pool di celle riutilizzabili (reuse queue), simile a UITableView. Il metodo dequeueReusableCell(withReuseIdentifier:for:) restituisce una cella dal pool o ne crea una nuova se il pool è vuoto. A differenza di UITableView, UICollectionViewCell ha un contentView incorporato in cui aggiungere tutte le subview — non aggiungerle direttamente alla cella, poiché contentView gestisce l'animazione corretta di selezione e modifica.
UICollectionViewFlowLayout — il layout standard che funziona da iOS 6. Dispone gli elementi in righe (flow) con la possibilità di configurare la dimensione della cella, la direzione di scorrimento, le spaziature minime e le sezioni. UICollectionViewCompositionalLayout — un layout moderno (iOS 13+) che descrive il layout attraverso una combinazione di blocchi: gruppo → sezione → layout, dove ogni gruppo può essere orizzontale, verticale o personalizzato.
| Parametro | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Versione minima | iOS 6 | iOS 13 |
| Layout complessi | Solo griglia o elenco semplice | Sezioni con layout diversi, gruppi, caroselli |
| Adattabilità | Tramite dimensione del delegato | NSCollectionLayoutDimension.fractionalWidth/Height |
| Prestazioni | Renderizza tutti gli elementi visibili | Creazione pigra delle sezioni |
| Supporto iPad | Richiede adattamento manuale | Gruppi adattivi tramite fractionalWidth |
| Complessità di implementazione | Bassa (5–10 righe) | Media (15–30 righe) |
// UICollectionView di base con 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
}
}
Raccomandazione Apple (WWDC 2025): usa CompositionalLayout per i nuovi progetti. Offre più capacità per layout adattivi, funziona meglio con UIKit e SwiftUI tramite UICollectionView.Representable e supporta intestazioni di sezione con dimensione automatica. Mantieni FlowLayout per schermi semplici con una griglia di celle identiche e versione minima iOS 12 e inferiore.
UICollectionViewCell — la classe base per le celle di UICollectionView. Ogni cella contiene contentView (contenitore principale), backgroundView (sfondo predefinito), selectedBackgroundView (sfondo selezionato). Configura l'aspetto della cella all'interno di contentView — non aggiungere mai subview direttamente a UICollectionViewCell.
// Cella personalizzata con configurazione
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)
// Vincoli di Auto Layout
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 fornisce metodi per gestire la selezione, l'evidenziazione, i menu contestuali e il riordinamento. A differenza di UITableView, UICollectionViewDelegate consente di controllare non solo i tap sugli elementi ma anche la pressione lunga (menu contestuale tramite UIContextMenuInteraction). Per gestire i tap, implementa collectionView(_:didSelectItemAt:) — viene chiamato al completamento dell'animazione di selezione.
UICollectionViewDiffableDataSource — una sostituzione type-safe per UICollectionViewDataSource introdotta in iOS 13. Invece di gestire manualmente le celle tramite cellForItemAt, DiffableDataSource utilizza NSDiffableDataSourceSnapshot — un'istantanea dello stato dei dati che calcola automaticamente la differenza tra lo stato vecchio e quello nuovo e anima le modifiche.
// DiffableDataSource con sezioni
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)
}
// Aggiornamento con animazione automatica
func updateProducts(_ products: [Product]) {
var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers)
snapshot.appendItems(products)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
Vantaggi di DiffableDataSource: (1) animazione automatica — insert, delete, reload avvengono con animazione predefinita; (2) sicurezza dei tipi — sezioni ed elementi sono tipizzati, errori di compilazione invece di crash runtime; (3) Snapshot — l'istantanea dello stato può essere salvata, annullata, confrontata; (4) prestazioni — DiffableDataSource utilizza l'algoritmo di Hellier per calcolare il set minimo di modifiche. Apple (WWDC 2024) raccomanda DiffableDataSource per tutti i nuovi UICollectionView e di mantenere il vecchio DataSource solo per iOS 12 e versioni precedenti.
UICollectionViewLayout — una classe astratta che controlla completamente il posizionamento degli elementi della collezione. Se FlowLayout e CompositionalLayout non sono adatti (ad esempio, layout circolare, spirale, diagramma), crea una sottoclasse di UICollectionViewLayout e sovrascrivi i metodi di preparazione del layout.
// Layout circolare personalizzato
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]
}
}
Prestazioni: un UICollectionViewLayout personalizzato deve memorizzare nella cache gli attributi in prepare() e restituirli in O(1) in layoutAttributesForItem(at:). Per collezioni con 1000+ elementi, usa aggiornamenti incrementali della cache tramite shouldInvalidateLayout(forBoundsChange:) — questo evita di ricalcolare l'intero layout durante lo scorrimento. Apple (WWDC 2023) raccomanda CompositionalLayout invece dei layout personalizzati: copre il 95% degli scenari senza dover scrivere un layout da zero.
UITableView e UICollectionView sono i due componenti principali di UIKit per visualizzare elenchi. UITableView è più semplice e progettato per elenchi a colonna singola. UICollectionView è più flessibile e supporta qualsiasi layout. La scelta tra loro dipende dalla struttura visiva dei dati e dalla personalizzazione richiesta.
| Parametro | UITableView | UICollectionView |
|---|---|---|
| Layout | Solo elenco verticale | Elenco, griglia, cascata, carosello, personalizzato |
| Complessità | Bassa (DataSource semplice) | Media (Layout + DataSource + Delegate) |
| Cella predefinita | 4 stili (basic, subtitle, value1, value2) | Solo vuota (personalizzata tramite contentView) |
| Modalità modifica | Integrata (delete, move, insert) | Richiede implementazione manuale |
| Header/Footer | Integrati (viewForHeaderInSection) | Tramite supplementary views |
| Prestazioni | Alte per elenchi | Alte per qualsiasi layout |
| iOS 6+ | Disponibile da iOS 2 | Disponibile da iOS 6 |
Regola pratica: usa UITableView se lo schermo è un semplice elenco di righe (impostazioni, contatti, menu). Usa UICollectionView se lo schermo richiede una griglia, scorrimento orizzontale, carosello, diverse dimensioni delle celle o raggruppamenti complessi. Da iOS 14, UITableView ha anche ottenuto il supporto di CompositionalLayout tramite UICollectionLayoutListConfiguration, consentendo di combinare i vantaggi di entrambi i componenti.
Domande frequenti
Imposta scrollDirection = .horizontal in UICollectionViewFlowLayout. Per CompositionalLayout, usa NSCollectionLayoutSize con fractionalWidth per la larghezza adattiva del gruppo. Lo scorrimento orizzontale è adatto per caroselli, gallerie e categorie. Per lo scorrimento paginato, imposta collectionView.isPagingEnabled = true o usa UICollectionViewFlowLayout con itemSize = bounds.width.
Verifica: (1) la cella è registrata tramite register(_:forCellWithReuseIdentifier:), (2) DataSource restituisce un conteggio elementi > 0, (3) la cella viene dequeue con il corretto reuseIdentifier, (4) itemSize non è zero (per FlowLayout). Un errore tipico è itemSize = CGSize.zero, che rende le celle con altezza zero. Imposta itemSize esplicitamente o implementa il delegato collectionView(_:layout:sizeForItemAt:).
Per FlowLayout: layout.minimumInteritemSpacing (tra elementi in una riga) e layout.minimumLineSpacing (tra righe). Per CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing e contentInsets sul gruppo/sezione. sectionInset imposta i margini esterni della sezione. Usa sectionInsetReference = .fromContentInset per la corretta gestione della Safe Area.
Usa performBatchUpdates per animazioni di gruppo: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Per aggiornamenti sicuri e animati, usa UICollectionViewDiffableDataSource: chiama dataSource.apply(snapshot, animatingDifferences: true) — tutte le modifiche verranno calcolate e applicate automaticamente senza insert/delete manuali.
In SwiftUI, gli equivalenti di UICollectionView sono LazyVGrid e LazyHGrid (iOS 14+). SwiftUI Grid è più semplice da scrivere (sintassi dichiarativa) ma inferiore a UICollectionView in prestazioni per 500+ elementi e personalizzazione dei layout. Per collezioni complesse, usa UICollectionView tramite UIViewRepresentable. Nei progetti IT Sectr, scegliamo UICollectionView per cataloghi e gallerie e SwiftUI Grid per schermi semplici.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche