UICollectionView
iOS Development
Swift
Center Align
Mobile App Design

How to center align the cells of a UICollectionView?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UICollectionViewFlowLayout lays out items from left to right by default, so rows usually hug the leading edge of the container. If you want each row of cells centered horizontally, you need to adjust the layout attributes after the flow layout computes them.

Why The Default Flow Layout Is Not Enough

The built-in flow layout knows item size, spacing, and section insets, but it does not automatically recenter a partially filled row. If a row contains only two narrow items, they still start at the left inset rather than being centered in the available width.

That is why most solutions subclass UICollectionViewFlowLayout and rewrite the x positions of items row by row.

A Custom Centering Flow Layout

swift
1import UIKit
2
3final class CenterAlignedFlowLayout: UICollectionViewFlowLayout {
4    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
5        guard let attributes = super.layoutAttributesForElements(in: rect) else {
6            return nil
7        }
8
9        let copiedAttributes = attributes.map { $0.copy() as! UICollectionViewLayoutAttributes }
10        let cellAttributes = copiedAttributes.filter { $0.representedElementCategory == .cell }
11
12        var rows: [[UICollectionViewLayoutAttributes]] = []
13        for attribute in cellAttributes {
14            if let lastRow = rows.last,
15               let first = lastRow.first,
16               abs(first.frame.minY - attribute.frame.minY) < 1 {
17                rows[rows.count - 1].append(attribute)
18            } else {
19                rows.append([attribute])
20            }
21        }
22
23        for row in rows {
24            let rowWidth = row.reduce(0) { $0 + $1.frame.width } + CGFloat(max(row.count - 1, 0)) * minimumInteritemSpacing
25            let availableWidth = collectionView!.bounds.width - sectionInset.left - sectionInset.right
26            var x = sectionInset.left + (availableWidth - rowWidth) / 2
27
28            for attribute in row {
29                attribute.frame.origin.x = x
30                x += attribute.frame.width + minimumInteritemSpacing
31            }
32        }
33
34        return copiedAttributes
35    }
36}

This groups visible cells by row, calculates the row width, and shifts the row horizontally so it sits in the center.

Using The Layout

swift
1let layout = CenterAlignedFlowLayout()
2layout.minimumInteritemSpacing = 8
3layout.minimumLineSpacing = 8
4layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
5
6let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

If your cells are fixed width, that may be all you need.

Self-Sizing Cells Need Extra Care

If the cell width comes from Auto Layout, use estimated sizing so the flow layout knows the final widths before centering.

swift
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

With self-sizing cells, layout invalidation matters. The collection view may need to re-layout after content changes so the centering calculation uses final item widths instead of outdated estimates.

Section Insets And Spacing Still Matter

Centering a row does not replace section insets. Insets still define the safe drawing margins, while the centering logic works inside that available width.

A common mistake is to set left and right insets to zero and then wonder why the row feels too close to the edges. The centering algorithm will honor whatever insets you configure.

Compositional Layout Is Different

If your design is built with UICollectionViewCompositionalLayout, you often solve horizontal alignment at the group level instead. But for classic flow-layout grids with dynamic row content, a custom UICollectionViewFlowLayout subclass is still a very practical solution.

Invalidate On Bounds Changes

Centering can break after rotation if the layout is not recalculated for the new width.

swift
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    return true
}

Adding that override to the layout class helps keep alignment correct when the collection view width changes.

Common Pitfalls

The most common mistake is modifying the original layout attributes from super instead of working on copies, which can produce warnings or unstable layout behavior. Another is grouping rows by exact floating-point equality on minY; a small tolerance is safer. Developers also often forget that centering should ignore headers and supplementary views, so the logic should filter for .cell attributes only. Finally, if the collection view width changes after rotation, the rows must be invalidated and recomputed.

Summary

  • Default flow layout left-aligns rows, even when the last row is short.
  • Centering rows usually requires a custom UICollectionViewFlowLayout subclass.
  • Group cells by row, compute row width, and shift the row horizontally.
  • Self-sizing cells and rotation require correct invalidation behavior.
  • Filter to cell attributes only so supplementary views are not repositioned incorrectly.

Course illustration
Course illustration

All Rights Reserved.