UICollectionView
iOS Development
Horizontal Scrolling
Swift
Mobile App Design

UICollectionView - Horizontal scroll, horizontal layout?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Creating a horizontally scrolling UICollectionView requires setting scrollDirection = .horizontal on the UICollectionViewFlowLayout. By default, UICollectionView scrolls vertically and lays out items left-to-right, wrapping to the next row. Changing to horizontal scroll makes items flow top-to-bottom and wrap to the next column. For a single horizontal row (carousel), set the item height equal to the collection view height. This article covers basic horizontal layout, paging, and compositional layout approaches.

Basic Horizontal Layout

swift
1import UIKit
2
3class HorizontalCollectionVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
4
5    var collectionView: UICollectionView!
6    let items = Array(1...20)
7
8    override func viewDidLoad() {
9        super.viewDidLoad()
10
11        let layout = UICollectionViewFlowLayout()
12        layout.scrollDirection = .horizontal
13        layout.minimumLineSpacing = 10
14        layout.minimumInteritemSpacing = 10
15        layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
16
17        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
18        collectionView.dataSource = self
19        collectionView.delegate = self
20        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
21        collectionView.backgroundColor = .systemBackground
22
23        view.addSubview(collectionView)
24    }
25
26    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
27        return items.count
28    }
29
30    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
31        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
32        cell.backgroundColor = .systemBlue
33        return cell
34    }
35
36    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
37        return CGSize(width: 120, height: 120)
38    }
39}

Setting scrollDirection = .horizontal on the flow layout is the key configuration. Items fill vertically first, then scroll horizontally to show more columns.

swift
1class CarouselVC: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
2
3    override func viewDidLoad() {
4        super.viewDidLoad()
5
6        let layout = UICollectionViewFlowLayout()
7        layout.scrollDirection = .horizontal
8        layout.minimumLineSpacing = 16
9
10        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
11        collectionView.translatesAutoresizingMaskIntoConstraints = false
12        collectionView.showsHorizontalScrollIndicator = false
13        collectionView.dataSource = self
14        collectionView.delegate = self
15        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
16
17        view.addSubview(collectionView)
18        NSLayoutConstraint.activate([
19            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
20            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
21            collectionView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
22            collectionView.heightAnchor.constraint(equalToConstant: 200),
23        ])
24    }
25
26    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
27        // Height matches collection view height minus insets for single row
28        return CGSize(width: 150, height: 180)
29    }
30
31    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 10 }
32
33    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
34        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
35        cell.backgroundColor = .systemIndigo
36        cell.layer.cornerRadius = 12
37        return cell
38    }
39}

For a single horizontal row, set the item height to fill the collection view's height (minus section insets). This prevents vertical wrapping.

Paging Behavior

swift
1let layout = UICollectionViewFlowLayout()
2layout.scrollDirection = .horizontal
3layout.minimumLineSpacing = 0
4
5let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
6collectionView.isPagingEnabled = true
7collectionView.showsHorizontalScrollIndicator = false
8
9// Each item fills the entire visible area for full-page swiping
10func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
11    return collectionView.bounds.size
12}

isPagingEnabled = true snaps scrolling to full-page boundaries. Set minimumLineSpacing = 0 and item size equal to collection view size for clean page transitions.

Compositional Layout (iOS 13+)

swift
1func createHorizontalLayout() -> UICollectionViewCompositionalLayout {
2    let itemSize = NSCollectionLayoutSize(
3        widthDimension: .fractionalWidth(1.0),
4        heightDimension: .fractionalHeight(1.0)
5    )
6    let item = NSCollectionLayoutItem(layoutSize: itemSize)
7    item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
8
9    let groupSize = NSCollectionLayoutSize(
10        widthDimension: .absolute(150),
11        heightDimension: .absolute(200)
12    )
13    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
14
15    let section = NSCollectionLayoutSection(group: group)
16    section.orthogonalScrollingBehavior = .continuous  // Horizontal scroll
17
18    return UICollectionViewCompositionalLayout(section: section)
19}
20
21// Usage
22let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createHorizontalLayout())

UICollectionViewCompositionalLayout with .orthogonalScrollingBehavior creates horizontally scrolling sections within a vertically scrolling collection view — ideal for app-store-style layouts.

In Storyboard

swift
1// If using Interface Builder:
2// 1. Drag a UICollectionView onto the storyboard
3// 2. Select the Collection View
4// 3. In the Attributes Inspector, set "Scroll Direction" to "Horizontal"
5// 4. Adjust "Min Spacing" for line and item spacing
6
7// Or configure in code after outlet connection
8@IBOutlet weak var collectionView: UICollectionView!
9
10override func viewDidLoad() {
11    super.viewDidLoad()
12    if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
13        layout.scrollDirection = .horizontal
14    }
15}

Common Pitfalls

  • Items stacking vertically instead of scrolling: If items appear in a vertical column that scrolls horizontally, the item height is smaller than the collection view height. For a single row, make item height fill the available height.
  • minimumLineSpacing vs minimumInteritemSpacing: In horizontal scroll, minimumLineSpacing controls the horizontal gap between columns, and minimumInteritemSpacing controls the vertical gap between rows. The names are counterintuitive for horizontal layouts.
  • Cell size exceeding collection view bounds: If the item width exceeds the collection view width, cells overlap or disappear. Ensure item size fits within the visible area minus section insets.
  • isPagingEnabled with non-zero spacing: Paging assumes each page is exactly the collection view width. Non-zero minimumLineSpacing causes drift over multiple pages. Set spacing to 0 or use custom snapping with targetContentOffset.
  • Not hiding the scroll indicator: Horizontal carousels typically hide the scroll indicator with showsHorizontalScrollIndicator = false. The default shows a thin bar at the bottom that looks out of place in carousel UIs.

Summary

  • Set layout.scrollDirection = .horizontal on UICollectionViewFlowLayout for horizontal scrolling
  • For a single-row carousel, make item height match the collection view height
  • Enable isPagingEnabled with zero line spacing for full-page swipe behavior
  • Use UICollectionViewCompositionalLayout with .orthogonalScrollingBehavior for modern iOS 13+ layouts
  • minimumLineSpacing controls horizontal gaps in horizontal scroll direction

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.