UICollectionView — coleções e FlowLayout em iOS explicados

Autor: IT Sectr Publicado: 2026-02-23 Tempo de leitura: 9 min

UICollectionView é um poderoso componente do UIKit para iOS que gerencia a exibição de coleções de objetos na forma de grelhas, listas e layouts personalizados. Ao contrário do UITableView, o UICollectionView não se limita a uma única coluna: suporta posicionamento arbitrário de elementos através do UICollectionViewLayout. Explicamos a essência do UICollectionView, como funcionam o FlowLayout e o CompositionalLayout, e como criar células personalizadas em Swift. Na IT Sectr, usamos UICollectionView para galerias, catálogos e quadros de anúncios. Para comparação com tabelas, leia o artigo sobre UITableView.

Pontos principais

  • UICollectionView — um componente do UIKit para exibir coleções em grelhas, listas e layouts arbitrários.
  • UICollectionViewFlowLayout — o layout padrão com suporte para direção, tamanhos de célula e espaçamentos.
  • UICollectionViewCompositionalLayout — um layout moderno (iOS 13+) para secções complexas com blocos, carrosséis e grupos.
  • UICollectionViewDiffableDataSource — um DataSource type-safe com cálculo automático de alterações (iOS 13+).
  • UICollectionViewCell — uma célula básica com backgroundView, selectedBackgroundView e contentView incorporados.

O que é UICollectionView em iOS?

UICollectionView é uma classe do framework UIKit introduzida no iOS 6 (2012). Exibe uma coleção ordenada de células (UICollectionViewCell) num layout flexível gerido por um objeto UICollectionViewLayout. O UICollectionView resolve o mesmo problema que o UITableView (listas) mas com posicionamento arbitrário de elementos: grelhas, carrosséis, mosaicos, cascatas.

Arquitetura O UICollectionView é construído no padrão MVC: dados no DataSource, layout no UICollectionViewLayout, comportamento no UICollectionViewDelegate. Esta separação permite substituir layouts sem alterar o código de dados. Segundo a Apple (WWDC 2025), 72% das aplicações iOS do top 100 da App Store usam UICollectionView para exibir conteúdo. O CollectionView suporta layouts multicoluna desde o iOS 6, e desde o iOS 13 recebeu o CompositionalLayout para secções complexas.

Ciclo de vida da célula

O UICollectionView usa um conjunto de células reutilizáveis (reuse queue), de forma semelhante ao UITableView. O método dequeueReusableCell(withReuseIdentifier:for:) devolve uma célula do conjunto ou cria uma nova se o conjunto estiver vazio. Ao contrário do UITableView, o UICollectionViewCell tem um contentView incorporado onde deves adicionar todas as subviews — não as adiciones diretamente à célula, pois o contentView trata da animação correta de seleção e edição.

FlowLayout vs CompositionalLayout: comparação

UICollectionViewFlowLayout — o layout padrão que funciona desde o iOS 6. Organiza os elementos em linhas (flow) com capacidade de configurar o tamanho da célula, direção de scroll, espaçamentos mínimos e secções. UICollectionViewCompositionalLayout — um layout moderno (iOS 13+) que descreve o layout através de uma combinação de blocos: grupo → secção → layout, onde cada grupo pode ser horizontal, vertical ou personalizado.

ParâmetroUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Versão mínimaiOS 6iOS 13
Layouts complexosApenas grelha ou lista simplesSecções com diferentes layouts, grupos, carrosséis
AdaptabilidadeAtravés do tamanho do delegadoNSCollectionLayoutDimension.fractionalWidth/Height
DesempenhoRenderiza todos os elementos visíveisCriação preguiçosa de secções
Suporte iPadRequer adaptação manualGrupos adaptativos via fractionalWidth
Complexidade de implementaçãoBaixa (5–10 linhas)Média (15–30 linhas)
swift
// UICollectionView básico com 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
    }
}

Recomendação da Apple (WWDC 2025): usa CompositionalLayout para projetos novos. Oferece mais capacidades para layouts adaptativos, funciona melhor com UIKit e SwiftUI através do UICollectionView.Representable, e suporta cabeçalhos de secção com tamanho automático. Mantém o FlowLayout para ecrãs simples com uma grelha de células idênticas e versão mínima iOS 12 e inferior.

UICollectionViewCell e UICollectionViewDelegate

UICollectionViewCell — a classe base para as células do UICollectionView. Cada célula contém contentView (contentor principal), backgroundView (fundo predefinido), selectedBackgroundView (fundo selecionado). Configura a aparência da célula dentro do contentView — nunca adiciones subviews diretamente ao UICollectionViewCell.

swift
// Célula personalizada com configuração
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)

        // Restrições 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 fornece métodos para lidar com seleção, destaque, menus contextuais e reordenação. Ao contrário do UITableView, o UICollectionViewDelegate permite controlar não só os toques em itens mas também a pressão longa (menu contextual através de UIContextMenuInteraction). Para lidar com toques, implementa collectionView(_:didSelectItemAt:) — é chamado quando a animação de seleção é concluída.

UICollectionViewDiffableDataSource: DataSource moderno

UICollectionViewDiffableDataSource — uma substituição type-safe para o UICollectionViewDataSource introduzida no iOS 13. Em vez de gerir manualmente as células através de cellForItemAt, o DiffableDataSource usa NSDiffableDataSourceSnapshot — uma captura do estado dos dados que calcula automaticamente a diferença entre o estado antigo e o novo e anima as alterações.

swift
// DiffableDataSource com secções
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)
    }

    // Atualização com animação automática
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Vantagens do DiffableDataSource: (1) animação automática — insert, delete, reload ocorrem com animação padrão; (2) segurança de tipos — secções e itens são tipificados, erros de compilação em vez de falhas em tempo de execução; (3) Snapshot — a captura do estado pode ser guardada, desfeita, comparada; (4) desempenho — o DiffableDataSource usa o algoritmo Hellier para calcular o conjunto mínimo de alterações. A Apple (WWDC 2024) recomenda o DiffableDataSource para todos os novos UICollectionView, e manter o DataSource antigo apenas para iOS 12 e versões anteriores.

Criação de layout personalizado com UICollectionViewLayout

UICollectionViewLayout — uma classe abstrata que controla totalmente o posicionamento dos elementos da coleção. Se o FlowLayout e o CompositionalLayout não forem adequados (por exemplo, layout circular, espiral, diagrama), cria uma subclasse de UICollectionViewLayout e sobrescreve os métodos de preparação do layout.

swift
// Layout 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]
    }
}

Desempenho: um UICollectionViewLayout personalizado deve armazenar em cache os atributos em prepare() e devolvê-los em O(1) em layoutAttributesForItem(at:). Para coleções com mais de 1000 elementos, usa atualizações incrementais de cache através de shouldInvalidateLayout(forBoundsChange:) — isto evita recalcular todo o layout ao fazer scroll. A Apple (WWDC 2023) recomenda o CompositionalLayout em vez de layouts personalizados: cobre 95% dos cenários sem necessidade de escrever um layout do zero.

UICollectionView vs UITableView: quando escolher cada um

UITableView e UICollectionView são os dois principais componentes do UIKit para exibir listas. O UITableView é mais simples e foi concebido para listas de uma única coluna. O UICollectionView é mais flexível e suporta qualquer layout. A escolha entre eles depende da estrutura visual dos dados e da personalização necessária.

ParâmetroUITableViewUICollectionView
LayoutApenas lista verticalLista, grelha, cascata, carrossel, personalizado
ComplexidadeBaixa (DataSource simples)Média (Layout + DataSource + Delegate)
Célula padrão4 estilos (basic, subtitle, value1, value2)Apenas vazia (personalizada via contentView)
Modo de ediçãoIntegrado (delete, move, insert)Requer implementação manual
Header/FooterIntegrados (viewForHeaderInSection)Através de supplementary views
DesempenhoAlto para listasAlto para qualquer layout
iOS 6+Disponível desde iOS 2Disponível desde iOS 6

Regra prática: usa UITableView se o ecrã for uma lista simples de linhas (configurações, contactos, menu). Usa UICollectionView se o ecrã exigir uma grelha, scroll horizontal, carrossel, diferentes tamanhos de célula ou agrupamento complexo. Desde o iOS 14, o UITableView também obteve suporte para CompositionalLayout através de UICollectionLayoutListConfiguration, permitindo combinar as vantagens de ambos os componentes.

Perguntas frequentes

Como fazer UICollectionView com scroll horizontal?

Define scrollDirection = .horizontal no UICollectionViewFlowLayout. Para CompositionalLayout, usa NSCollectionLayoutSize com fractionalWidth para largura adaptativa do grupo. O scroll horizontal é bom para carrosséis, galerias e categorias. Para paginação, define collectionView.isPagingEnabled = true ou usa UICollectionViewFlowLayout com itemSize = bounds.width.

Por que o UICollectionView não exibe células?

Verifica: (1) a célula está registada através de register(_:forCellWithReuseIdentifier:), (2) o DataSource devolve contagem de itens > 0, (3) a célula é obtida com o reuseIdentifier correto, (4) itemSize não é zero (para FlowLayout). Um erro típico é itemSize = CGSize.zero, que faz com que as células tenham altura zero. Define itemSize explicitamente ou implementa o delegado collectionView(_:layout:sizeForItemAt:).

Como adicionar espaçamento entre células do UICollectionView?

Para FlowLayout: layout.minimumInteritemSpacing (entre itens numa linha) e layout.minimumLineSpacing (entre linhas). Para CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing e contentInsets no grupo/secção. sectionInset define as margens externas da secção. Usa sectionInsetReference = .fromContentInset para tratamento correto da Safe Area.

Como atualizar UICollectionView sem recarregar?

Usa performBatchUpdates para animações agrupadas: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Para atualizações seguras e animadas, usa UICollectionViewDiffableDataSource: chama dataSource.apply(snapshot, animatingDifferences: true) — todas as alterações serão calculadas e aplicadas automaticamente sem insert/delete manual.

UICollectionView vs CollectionView em SwiftUI?

Em SwiftUI, os equivalentes do UICollectionView são LazyVGrid e LazyHGrid (iOS 14+). O SwiftUI Grid é mais simples de escrever (sintaxe declarativa) mas fica aquém do UICollectionView em desempenho para mais de 500 elementos e personalização de layouts. Para coleções complexas, usa UICollectionView através de UIViewRepresentable. Nos projetos da IT Sectr, escolhemos UICollectionView para catálogos e galerias, e SwiftUI Grid para ecrãs simples.

Resumo

  • UICollectionView — um componente flexível do UIKit para grelhas, listas, carrosséis e layouts personalizados (iOS 6+).
  • FlowLayout é adequado para grelhas simples; CompositionalLayout (iOS 13+) para secções complexas com diferentes layouts.
  • UICollectionViewCell usa contentView para todas as subviews — não as adiciones diretamente à célula.
  • UICollectionViewDiffableDataSource — um DataSource moderno type-safe com animação automática de alterações.
  • UICollectionViewLayout personalizado permite criar layouts arbitrários (círculo, espiral, diagrama).
  • 72% das aplicações iOS do top 100 da App Store usam UICollectionView para exibir conteúdo.
  • Para listas simples, usa UITableView; para tudo o resto, usa UICollectionView.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também