UICollectionView
cell size
device orientation
iOS development
Swift programming

Change UICollectionViewCell size on different device orientations

Master System Design with Codemia

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

Introduction

To change UICollectionViewCell size when the device rotates, the important input is usually the collection view's current width, not the raw orientation enum. A layout that depends on actual bounds is more reliable because split view, multitasking, and container sizing can all change width without behaving like a simple portrait-versus-landscape switch.

Use UICollectionViewDelegateFlowLayout

With UICollectionViewFlowLayout, the normal place to size cells is collectionView(_:layout:sizeForItemAt:).

swift
1import UIKit
2
3final class PhotosViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
4    @IBOutlet private weak var collectionView: UICollectionView!
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        collectionView.dataSource = self
9        collectionView.delegate = self
10    }
11
12    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
13        return 20
14    }
15
16    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
17        return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
18    }
19
20    func collectionView(_ collectionView: UICollectionView,
21                        layout collectionViewLayout: UICollectionViewLayout,
22                        sizeForItemAt indexPath: IndexPath) -> CGSize {
23        let columns: CGFloat = collectionView.bounds.width > collectionView.bounds.height ? 4 : 2
24        let spacing: CGFloat = 10
25        let totalSpacing = spacing * (columns - 1)
26        let width = (collectionView.bounds.width - totalSpacing) / columns
27        return CGSize(width: width, height: width)
28    }
29}

This example uses more columns when the view is wider than it is tall. The calculation is based on the collection view's actual bounds, which is what the layout really cares about.

Invalidate the Layout on Size Changes

When the interface rotates, the layout needs to be invalidated so the collection view asks for new sizes.

swift
1override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
2    super.viewWillTransition(to: size, with: coordinator)
3
4    coordinator.animate(alongsideTransition: { _ in
5        self.collectionView.collectionViewLayout.invalidateLayout()
6    })
7}

Without invalidation, the old cached layout can remain visible longer than you expect.

Why Bounds-Based Logic Is Better Than Orientation Checks

Many examples use UIDevice.current.orientation, but that is often the wrong signal for layout decisions. Problems include:

  • the device may report .faceUp or .unknown
  • split-screen changes width without a clean orientation story
  • container views can resize independently of device orientation

A cell layout cares about available width and height, so use those directly.

Account for Insets and Spacing

Real sizing usually needs section insets and inter-item spacing. If you ignore them, cells may overflow or create uneven gaps.

Here is a more complete calculation:

swift
1func collectionView(_ collectionView: UICollectionView,
2                    layout collectionViewLayout: UICollectionViewLayout,
3                    sizeForItemAt indexPath: IndexPath) -> CGSize {
4    let columns: CGFloat = collectionView.bounds.width > collectionView.bounds.height ? 4 : 2
5    let inset: CGFloat = 16
6    let spacing: CGFloat = 12
7    let totalHorizontalPadding = inset * 2 + spacing * (columns - 1)
8    let width = (collectionView.bounds.width - totalHorizontalPadding) / columns
9    return CGSize(width: floor(width), height: floor(width * 1.2))
10}

If your flow layout also sets sectionInset and spacing properties, keep the math consistent with those values.

Consider Subclassing the Layout for Complex Cases

If sizing logic becomes more architectural than per-cell, a custom layout or a flow-layout subclass may be better than keeping all calculations in the delegate method. That is especially true when:

  • columns depend on size classes
  • headers and footers change too
  • you want one reusable layout policy across screens

For normal grid adjustments, though, the delegate method is usually enough.

Rotation Is Not the Only Trigger

Even if the question mentions device orientation, remember that size changes can also come from:

  • iPad multitasking
  • embedding the collection view inside another resized container
  • dynamic safe-area changes

That is another reason bounds-based sizing plus layout invalidation is more robust than an orientation notification alone.

Common Pitfalls

The most common mistake is using UIDevice.current.orientation directly to decide layout. That value can be noisy or irrelevant compared with the actual collection-view width.

Another issue is recalculating sizes correctly but forgetting to invalidate the layout when bounds change. Then the delegate logic is fine, but the visible cells do not update.

People also forget to subtract spacing and insets from the width calculation, which makes cells too wide and causes layout glitches.

Summary

  • Size UICollectionViewCell items from the collection view's bounds, not raw device orientation alone.
  • Use collectionView(_:layout:sizeForItemAt:) for standard flow-layout sizing.
  • Invalidate the layout during size transitions so new item sizes are requested.
  • Subtract section insets and spacing when calculating widths.
  • Build for view-size changes in general, not only portrait-versus-landscape rotation.

Course illustration
Course illustration

All Rights Reserved.