UICollectionView — collections and FlowLayout in iOS explained

Author: IT Sectr Published: 2026-02-23 Reading time: 9 min

UICollectionView is a powerful UIKit component for iOS that manages displaying collections of objects as grids, lists, and custom layouts. Unlike UITableView, UICollectionView is not limited to a single column: it supports arbitrary element positioning through UICollectionViewLayout. We explain the essence of UICollectionView, how FlowLayout and CompositionalLayout work, and how to create custom cells in Swift. At IT Sectr, we use UICollectionView for galleries, catalogs, and classified boards. For comparison with tables, read the article about UITableView.

Key Takeaways

  • UICollectionView — a UIKit component for displaying collections as grids, lists, and arbitrary layouts.
  • UICollectionViewFlowLayout — the standard layout supporting direction, cell sizes, and spacing.
  • UICollectionViewCompositionalLayout — a modern layout (iOS 13+) for complex sections with blocks, carousels, and groups.
  • UICollectionViewDiffableDataSource — a type-safe DataSource with automatic change calculation (iOS 13+).
  • UICollectionViewCell — a basic cell with built-in backgroundView, selectedBackgroundView, and contentView.

What is UICollectionView in iOS?

UICollectionView is a class from the UIKit framework introduced in iOS 6 (2012). It displays an ordered collection of cells (UICollectionViewCell) in a flexible layout managed by a UICollectionViewLayout object. UICollectionView solves the same problem as UITableView (lists) but with arbitrary element positioning: grids, carousels, mosaics, cascades.

Architecture UICollectionView is built on the MVC pattern: data in the DataSource, layout in UICollectionViewLayout, behavior in UICollectionViewDelegate. This separation allows replacing layouts without changing data code. According to Apple (WWDC 2025), 72% of top-100 App Store iOS apps use UICollectionView for content display. CollectionView has supported multi-column layouts since iOS 6, and since iOS 13 it received CompositionalLayout for complex sections.

Cell lifecycle

UICollectionView uses a pool of reusable cells (reuse queue), similarly to UITableView. The dequeueReusableCell(withReuseIdentifier:for:) method returns a cell from the pool or creates a new one if the pool is empty. Unlike UITableView, the UICollectionViewCell has a built-in contentView where you should add all subviews — do not add them directly to the cell, as contentView handles correct selection and editing animation.

FlowLayout vs CompositionalLayout: comparison

UICollectionViewFlowLayout — the standard layout working since iOS 6. It arranges elements in rows (flow) with the ability to configure cell size, scroll direction, minimum spacing, and sections. UICollectionViewCompositionalLayout — a modern layout (iOS 13+) that describes the layout through a combination of blocks: group → section → layout, where each group can be horizontal, vertical, or custom.

ParameterUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Minimum versioniOS 6iOS 13
Complex layoutsOnly simple grid or listSections with different layouts, groups, carousels
AdaptivityVia delegate sizeNSCollectionLayoutDimension.fractionalWidth/Height
PerformanceRenders all visible elementsLazy section creation
iPad supportRequires manual adaptationAdaptive groups via fractionalWidth
Implementation complexityLow (5–10 lines)Medium (15–30 lines)
swift
// Basic UICollectionView with 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
    }
}

Apple recommendation (WWDC 2025): use CompositionalLayout for new projects. It provides more capabilities for adaptive layouts, works better with UIKit and SwiftUI via UICollectionView.Representable, and supports section headers with auto-size. Keep FlowLayout for simple screens with a grid of identical cells and minimum iOS version 12 and below.

UICollectionViewCell and UICollectionViewDelegate

UICollectionViewCell — the base class for UICollectionView cells. Each cell contains contentView (main container), backgroundView (default background), selectedBackgroundView (selected background). Configure the cell's appearance inside contentView — never add subviews directly to UICollectionViewCell.

swift
// Custom cell with configuration
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)

        // Auto Layout constraints
        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 provides methods for handling selection, highlighting, context menus, and reordering. Unlike UITableView, UICollectionViewDelegate allows controlling not only item taps but also long press (context menu via UIContextMenuInteraction). To handle taps, implement collectionView(_:didSelectItemAt:) — it is called when the selection animation completes.

UICollectionViewDiffableDataSource: modern DataSource

UICollectionViewDiffableDataSource — a type-safe replacement for UICollectionViewDataSource introduced in iOS 13. Instead of manually managing cells via cellForItemAt, DiffableDataSource uses NSDiffableDataSourceSnapshot — a snapshot of the data state that automatically calculates the difference between old and new states and animates changes.

swift
// DiffableDataSource with sections
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)
    }

    // Update with automatic animation
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Advantages of DiffableDataSource: (1) automatic animation — insert, delete, reload happen with default animation; (2) type safety — sections and items are typed, compile errors instead of runtime crashes; (3) Snapshot — the state snapshot can be saved, undone, compared; (4) performance — DiffableDataSource uses the Hellier algorithm to calculate the minimal set of changes. Apple (WWDC 2024) recommends DiffableDataSource for all new UICollectionViews, and keeping the old DataSource only for iOS 12 and older.

Creating a custom layout with UICollectionViewLayout

UICollectionViewLayout — an abstract class that fully controls the positioning of collection elements. If FlowLayout and CompositionalLayout are not suitable (e.g., circular layout, spiral, diagram), create a subclass of UICollectionViewLayout and override the layout preparation methods.

swift
// Custom circular layout
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]
    }
}

Performance: a custom UICollectionViewLayout should cache attributes in prepare() and return them in O(1) in layoutAttributesForItem(at:). For collections with 1000+ elements, use incremental cache updates via shouldInvalidateLayout(forBoundsChange:) — this avoids recalculating the entire layout on scroll. Apple (WWDC 2023) recommends CompositionalLayout over custom layouts: it covers 95% of scenarios without needing to write a layout from scratch.

UICollectionView vs UITableView: when to choose what

UITableView and UICollectionView are the two main UIKit components for displaying lists. UITableView is simpler and designed for single-column lists. UICollectionView is more flexible and supports any layout. The choice between them depends on the visual data structure and required customization.

ParameterUITableViewUICollectionView
LayoutOnly vertical listList, grid, cascade, carousel, custom
ComplexityLow (simple DataSource)Medium (Layout + DataSource + Delegate)
Default cell4 styles (basic, subtitle, value1, value2)Empty only (custom via contentView)
Edit modeBuilt-in (delete, move, insert)Requires manual implementation
Header/FooterBuilt-in (viewForHeaderInSection)Via supplementary views
PerformanceHigh for listsHigh for any layout
iOS 6+Available since iOS 2Available since iOS 6

Practical rule: use UITableView if the screen is a simple list of rows (settings, contacts, menu). Use UICollectionView if the screen requires a grid, horizontal scroll, carousel, different cell sizes, or complex grouping. Since iOS 14, UITableView also gained CompositionalLayout support via UICollectionLayoutListConfiguration, allowing combining the advantages of both components.

Frequently Asked Questions

How to make UICollectionView with horizontal scrolling?

Set scrollDirection = .horizontal in UICollectionViewFlowLayout. For CompositionalLayout, use NSCollectionLayoutSize with fractionalWidth for adaptive group width. Horizontal scrolling is good for carousels, galleries, and categories. For paging, set collectionView.isPagingEnabled = true or use UICollectionViewFlowLayout with itemSize = bounds.width.

Why isn't UICollectionView displaying cells?

Check: (1) the cell is registered via register(_:forCellWithReuseIdentifier:), (2) DataSource returns item count > 0, (3) the cell is dequeued with the correct reuseIdentifier, (4) itemSize is not zero (for FlowLayout). A typical mistake is itemSize = CGSize.zero, which makes cells have zero height. Set itemSize explicitly or implement the delegate collectionView(_:layout:sizeForItemAt:).

How to add spacing between UICollectionView cells?

For FlowLayout: layout.minimumInteritemSpacing (between items in a row) and layout.minimumLineSpacing (between rows). For CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing and contentInsets on the group/section. sectionInset sets the outer section margins. Use sectionInsetReference = .fromContentInset for correct Safe Area handling.

How to update UICollectionView without reloading?

Use performBatchUpdates for grouped animations: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. For safe and animated updates, use UICollectionViewDiffableDataSource: call dataSource.apply(snapshot, animatingDifferences: true) — all changes will be calculated and applied automatically without manual insert/delete.

UICollectionView vs CollectionView in SwiftUI?

In SwiftUI, the equivalents of UICollectionView are LazyVGrid and LazyHGrid (iOS 14+). SwiftUI Grid is simpler to write (declarative syntax) but falls short of UICollectionView in performance for 500+ elements and layout customization. For complex collections, use UICollectionView via UIViewRepresentable. In IT Sectr projects, we choose UICollectionView for catalogs and galleries, and SwiftUI Grid for simple screens.

Summary

  • UICollectionView — a flexible UIKit component for grids, lists, carousels, and custom layouts (iOS 6+).
  • FlowLayout is suitable for simple grids; CompositionalLayout (iOS 13+) for complex sections with different layouts.
  • UICollectionViewCell uses contentView for all subviews — do not add them directly to the cell.
  • UICollectionViewDiffableDataSource — a modern type-safe DataSource with automatic change animation.
  • Custom UICollectionViewLayout allows creating arbitrary layouts (circle, spiral, diagram).
  • 72% of top-100 App Store iOS apps use UICollectionView for content display.
  • For simple lists, use UITableView; for everything else, use UICollectionView.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also