UICollectionView es un potente componente de UIKit para iOS que gestiona la visualización de colecciones de objetos en forma de cuadrículas, listas y diseños personalizados. A diferencia de UITableView, UICollectionView no se limita a una sola columna: admite la colocación arbitraria de elementos mediante UICollectionViewLayout. Explicamos la esencia de UICollectionView, cómo funcionan FlowLayout y CompositionalLayout, y cómo crear celdas personalizadas en Swift. En IT Sectr, usamos UICollectionView para galerías, catálogos y tablones de anuncios. Para comparar con tablas, lea el artículo sobre UITableView.
Puntos clave
UICollectionView es una clase del framework UIKit introducida en iOS 6 (2012). Muestra una colección ordenada de celdas (UICollectionViewCell) en un diseño flexible gestionado por un objeto UICollectionViewLayout. UICollectionView resuelve el mismo problema que UITableView (listas) pero con posicionamiento arbitrario de elementos: cuadrículas, carruseles, mosaicos, cascadas.
Arquitectura UICollectionView está construida sobre el patrón MVC: los datos en el DataSource, el diseño en UICollectionViewLayout, el comportamiento en UICollectionViewDelegate. Esta separación permite reemplazar diseños sin cambiar el código de datos. Según Apple (WWDC 2025), el 72% de las aplicaciones iOS del top 100 de App Store usan UICollectionView para mostrar contenido. CollectionView admite diseños de varias columnas desde iOS 6, y desde iOS 13 recibió CompositionalLayout para secciones complejas.
UICollectionView usa un grupo de celdas reutilizables (reuse queue), similar a UITableView. El método dequeueReusableCell(withReuseIdentifier:for:) devuelve una celda del grupo o crea una nueva si el grupo está vacío. A diferencia de UITableView, UICollectionViewCell tiene un contentView integrado donde debes agregar todas las subvistas — no las agregues directamente a la celda, ya que contentView se encarga de la animación correcta de selección y edición.
UICollectionViewFlowLayout — el diseño estándar que funciona desde iOS 6. Organiza los elementos en filas (flow) con la capacidad de configurar el tamaño de la celda, la dirección de desplazamiento, los espaciados mínimos y las secciones. UICollectionViewCompositionalLayout — un diseño moderno (iOS 13+) que describe el diseño mediante una combinación de bloques: grupo → sección → diseño, donde cada grupo puede ser horizontal, vertical o personalizado.
| Parámetro | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Versión mínima | iOS 6 | iOS 13 |
| Diseños complejos | Solo cuadrícula o lista simple | Secciones con diferentes diseños, grupos, carruseles |
| Adaptabilidad | A través del tamaño del delegado | NSCollectionLayoutDimension.fractionalWidth/Height |
| Rendimiento | Renderiza todos los elementos visibles | Creación perezosa de secciones |
| Soporte iPad | Requiere adaptación manual | Grupos adaptativos mediante fractionalWidth |
| Complejidad de implementación | Baja (5–10 líneas) | Media (15–30 líneas) |
// UICollectionView básico 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
}
}
Recomendación de Apple (WWDC 2025): usa CompositionalLayout para proyectos nuevos. Ofrece más capacidades para diseños adaptativos, funciona mejor con UIKit y SwiftUI a través de UICollectionView.Representable, y admite encabezados de sección con tamaño automático. Mantén FlowLayout para pantallas simples con una cuadrícula de celdas idénticas y versión mínima de iOS 12 e inferior.
UICollectionViewCell — la clase base para las celdas de UICollectionView. Cada celda contiene contentView (contenedor principal), backgroundView (fondo predeterminado), selectedBackgroundView (fondo seleccionado). Configura la apariencia de la celda dentro de contentView — nunca agregues subvistas directamente a UICollectionViewCell.
// Celda personalizada con configuración
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)
// Restricciones de 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 proporciona métodos para manejar la selección, el resaltado, los menús contextuales y la reordenación. A diferencia de UITableView, UICollectionViewDelegate permite controlar no solo los toques en elementos sino también la pulsación larga (menú contextual a través de UIContextMenuInteraction). Para manejar toques, implementa collectionView(_:didSelectItemAt:) — se llama cuando se completa la animación de selección.
UICollectionViewDiffableDataSource — un reemplazo con seguridad de tipos para UICollectionViewDataSource introducido en iOS 13. En lugar de gestionar manualmente las celdas mediante cellForItemAt, DiffableDataSource usa NSDiffableDataSourceSnapshot — una instantánea del estado de los datos que calcula automáticamente la diferencia entre el estado anterior y el nuevo y anima los cambios.
// DiffableDataSource con secciones
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)
}
// Actualización con animación automática
func updateProducts(_ products: [Product]) {
var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers)
snapshot.appendItems(products)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
Ventajas de DiffableDataSource: (1) animación automática — insert, delete, reload ocurren con animación predeterminada; (2) seguridad de tipos — las secciones y los elementos están tipificados, errores de compilación en lugar de fallos en tiempo de ejecución; (3) Snapshot — la instantánea del estado se puede guardar, deshacer, comparar; (4) rendimiento — DiffableDataSource usa el algoritmo Hellier para calcular el conjunto mínimo de cambios. Apple (WWDC 2024) recomienda DiffableDataSource para todos los UICollectionView nuevos, y mantener el DataSource antiguo solo para iOS 12 y versiones anteriores.
UICollectionViewLayout — una clase abstracta que controla completamente la colocación de los elementos de la colección. Si FlowLayout y CompositionalLayout no son adecuados (por ejemplo, diseño circular, espiral, diagrama), crea una subclase de UICollectionViewLayout y sobrescribe los métodos de preparación del diseño.
// Diseño circular personalizado
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]
}
}
Rendimiento: un UICollectionViewLayout personalizado debe almacenar en caché los atributos en prepare() y devolverlos en O(1) en layoutAttributesForItem(at:). Para colecciones con más de 1000 elementos, usa actualizaciones incrementales de caché mediante shouldInvalidateLayout(forBoundsChange:) — esto evita recalcular todo el diseño al desplazarse. Apple (WWDC 2023) recomienda CompositionalLayout en lugar de diseños personalizados: cubre el 95% de los escenarios sin necesidad de escribir un diseño desde cero.
UITableView y UICollectionView son los dos componentes principales de UIKit para mostrar listas. UITableView es más simple y está diseñado para listas de una sola columna. UICollectionView es más flexible y admite cualquier diseño. La elección entre ellos depende de la estructura visual de los datos y la personalización requerida.
| Parámetro | UITableView | UICollectionView |
|---|---|---|
| Diseño | Solo lista vertical | Lista, cuadrícula, cascada, carrusel, personalizado |
| Complejidad | Baja (DataSource simple) | Media (Layout + DataSource + Delegate) |
| Celda predeterminada | 4 estilos (basic, subtitle, value1, value2) | Solo vacía (personalizada mediante contentView) |
| Modo edición | Integrado (delete, move, insert) | Requiere implementación manual |
| Header/Footer | Integrados (viewForHeaderInSection) | A través de supplementary views |
| Rendimiento | Alto para listas | Alto para cualquier diseño |
| iOS 6+ | Disponible desde iOS 2 | Disponible desde iOS 6 |
Regla práctica: usa UITableView si la pantalla es una lista simple de filas (configuración, contactos, menú). Usa UICollectionView si la pantalla requiere una cuadrícula, desplazamiento horizontal, carrusel, diferentes tamaños de celda o agrupación compleja. Desde iOS 14, UITableView también ha obtenido soporte para CompositionalLayout mediante UICollectionLayoutListConfiguration, lo que permite combinar las ventajas de ambos componentes.
Preguntas frecuentes
Establece scrollDirection = .horizontal en UICollectionViewFlowLayout. Para CompositionalLayout, usa NSCollectionLayoutSize con fractionalWidth para el ancho adaptativo del grupo. El desplazamiento horizontal es bueno para carruseles, galerías y categorías. Para paginación, establece collectionView.isPagingEnabled = true o usa UICollectionViewFlowLayout con itemSize = bounds.width.
Verifica: (1) la celda está registrada mediante register(_:forCellWithReuseIdentifier:), (2) DataSource devuelve un recuento de elementos > 0, (3) la celda se obtiene con el reuseIdentifier correcto, (4) itemSize no es cero (para FlowLayout). Un error típico es itemSize = CGSize.zero, que hace que las celdas tengan altura cero. Establece itemSize explícitamente o implementa el delegado collectionView(_:layout:sizeForItemAt:).
Para FlowLayout: layout.minimumInteritemSpacing (entre elementos en una fila) y layout.minimumLineSpacing (entre filas). Para CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing y contentInsets en el grupo/sección. sectionInset establece los márgenes exteriores de la sección. Usa sectionInsetReference = .fromContentInset para un manejo correcto de Safe Area.
Usa performBatchUpdates para animaciones agrupadas: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Para actualizaciones seguras y animadas, usa UICollectionViewDiffableDataSource: llama a dataSource.apply(snapshot, animatingDifferences: true) — todos los cambios se calcularán y aplicarán automáticamente sin insert/delete manual.
En SwiftUI, los equivalentes de UICollectionView son LazyVGrid y LazyHGrid (iOS 14+). SwiftUI Grid es más simple de escribir (sintaxis declarativa) pero se queda corto frente a UICollectionView en rendimiento para más de 500 elementos y personalización de diseños. Para colecciones complejas, usa UICollectionView mediante UIViewRepresentable. En los proyectos de IT Sectr, elegimos UICollectionView para catálogos y galerías, y SwiftUI Grid para pantallas simples.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también