UICollectionView — colecciones y FlowLayout en iOS explicados

Autor: IT Sectr Publicado: 2026-02-23 Tiempo de lectura: 9 min

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 — un componente de UIKit para mostrar colecciones en forma de cuadrículas, listas y diseños arbitrarios.
  • UICollectionViewFlowLayout — el diseño estándar que admite dirección, tamaños de celda y espaciados.
  • UICollectionViewCompositionalLayout — un diseño moderno (iOS 13+) para secciones complejas con bloques, carruseles y grupos.
  • UICollectionViewDiffableDataSource — un DataSource con seguridad de tipos con cálculo automático de cambios (iOS 13+).
  • UICollectionViewCell — una celda básica con backgroundView, selectedBackgroundView y contentView integrados.

¿Qué es UICollectionView en iOS?

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.

Ciclo de vida de la celda

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.

FlowLayout vs CompositionalLayout: comparació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ámetroUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Versión mínimaiOS 6iOS 13
Diseños complejosSolo cuadrícula o lista simpleSecciones con diferentes diseños, grupos, carruseles
AdaptabilidadA través del tamaño del delegadoNSCollectionLayoutDimension.fractionalWidth/Height
RendimientoRenderiza todos los elementos visiblesCreación perezosa de secciones
Soporte iPadRequiere adaptación manualGrupos adaptativos mediante fractionalWidth
Complejidad de implementaciónBaja (5–10 líneas)Media (15–30 líneas)
swift
// 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 y UICollectionViewDelegate

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.

swift
// 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: DataSource moderno

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.

swift
// 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.

Creación de un diseño personalizado con UICollectionViewLayout

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.

swift
// 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.

UICollectionView vs UITableView: cuándo elegir cada uno

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ámetroUITableViewUICollectionView
DiseñoSolo lista verticalLista, cuadrícula, cascada, carrusel, personalizado
ComplejidadBaja (DataSource simple)Media (Layout + DataSource + Delegate)
Celda predeterminada4 estilos (basic, subtitle, value1, value2)Solo vacía (personalizada mediante contentView)
Modo ediciónIntegrado (delete, move, insert)Requiere implementación manual
Header/FooterIntegrados (viewForHeaderInSection)A través de supplementary views
RendimientoAlto para listasAlto para cualquier diseño
iOS 6+Disponible desde iOS 2Disponible 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

¿Cómo hacer UICollectionView con desplazamiento horizontal?

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.

¿Por qué UICollectionView no muestra las celdas?

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:).

¿Cómo agregar espacio entre las celdas de UICollectionView?

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.

¿Cómo actualizar UICollectionView sin recargar?

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.

UICollectionView vs CollectionView en SwiftUI?

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

  • UICollectionView — un componente flexible de UIKit para cuadrículas, listas, carruseles y diseños personalizados (iOS 6+).
  • FlowLayout es adecuado para cuadrículas simples; CompositionalLayout (iOS 13+) para secciones complejas con diferentes diseños.
  • UICollectionViewCell usa contentView para todas las subvistas — no las agregues directamente a la celda.
  • UICollectionViewDiffableDataSource — un DataSource moderno con seguridad de tipos y animación automática de cambios.
  • UICollectionViewLayout personalizado permite crear diseños arbitrarios (círculo, espiral, diagrama).
  • El 72% de las aplicaciones iOS del top 100 de App Store usan UICollectionView para mostrar contenido.
  • Para listas simples, usa UITableView; para todo lo demás, usa UICollectionView.

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.

Discutir el proyecto

Lea también