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 é 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.
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.
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âmetro | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Versão mínima | iOS 6 | iOS 13 |
| Layouts complexos | Apenas grelha ou lista simples | Secções com diferentes layouts, grupos, carrosséis |
| Adaptabilidade | Através do tamanho do delegado | NSCollectionLayoutDimension.fractionalWidth/Height |
| Desempenho | Renderiza todos os elementos visíveis | Criação preguiçosa de secções |
| Suporte iPad | Requer adaptação manual | Grupos adaptativos via fractionalWidth |
| Complexidade de implementação | Baixa (5–10 linhas) | Média (15–30 linhas) |
// 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 — 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.
// 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 — 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.
// 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.
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.
// 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.
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âmetro | UITableView | UICollectionView |
|---|---|---|
| Layout | Apenas lista vertical | Lista, grelha, cascata, carrossel, personalizado |
| Complexidade | Baixa (DataSource simples) | Média (Layout + DataSource + Delegate) |
| Célula padrão | 4 estilos (basic, subtitle, value1, value2) | Apenas vazia (personalizada via contentView) |
| Modo de edição | Integrado (delete, move, insert) | Requer implementação manual |
| Header/Footer | Integrados (viewForHeaderInSection) | Através de supplementary views |
| Desempenho | Alto para listas | Alto para qualquer layout |
| iOS 6+ | Disponível desde iOS 2 | Disponí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
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.
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:).
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.
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.
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
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.
Leia também