Popular Searches
Popular Course Categories
Popular Courses

Understanding UITableView and UICollectionView

Understanding UITableView and UICollectionView

Lists, Collections, and Dynamic Data Presentation

Understanding UITableView and UICollectionView in iOS Development

Course: iOS App Development

Topic: Understanding UITableView and UICollectionView

Course Link: iOS App Development Training

Demo Link: Register for a Course Demo

JustAcademy Homepage: Visit JustAcademy


1. Introduction

UITableView and UICollectionView are important UIKit components used to display collections of data in iOS applications. They support scrolling, reusable cells, user interaction, and dynamic content updates.

UITableView is commonly used for vertically arranged rows, such as contact lists, settings, messages, and task lists. UICollectionView supports more flexible layouts, including grids, horizontal carousels, galleries, and customized arrangements of items.

Learning Objectives

  • Understand the purpose of table views and collection views.
  • Learn the roles of data sources, delegates, cells, and layouts.
  • Create and configure reusable cells in UIKit.
  • Display dynamic data using arrays and model objects.
  • Handle selection, deletion, and other user interactions.
  • Understand custom layouts, performance, accessibility, and troubleshooting.

2. What Is UITableView?

UITableView is a UIKit view that presents data in a single column of rows. Each row is represented by a reusable UITableViewCell. Rows can be grouped into sections, and each section can optionally have a header and footer.

Common Use Cases

  • Contact lists and address books.
  • Chat conversations and notification lists.
  • Settings screens and menu options.
  • Shopping cart items and order histories.
  • Task lists and records fetched from a database.

Main Components of UITableView

Component Purpose
UITableView Displays rows and manages scrolling.
UITableViewCell Displays the content of an individual row.
UITableViewDataSource Provides the number of sections, rows, and configured cells.
UITableViewDelegate Handles selection, row heights, display events, and other interactions.
IndexPath Identifies a row using its section and row positions.
Section header and footer Provide labels or supplementary information for a section.

3. What Is UICollectionView?

UICollectionView is a UIKit component that displays items using a configurable layout. Unlike a table view's standard single-column structure, a collection view can arrange items vertically, horizontally, in grids, or in more advanced layouts.

Common Use Cases

  • Photo galleries and image grids.
  • Product catalogs and shopping categories.
  • Horizontal cards and featured-content carousels.
  • Video thumbnails and media libraries.
  • Dashboards, calendars, and custom item arrangements.

Main Components of UICollectionView

Component Purpose
UICollectionView Displays and manages a collection of items.
UICollectionViewCell Displays the content of an individual item.
UICollectionViewDataSource Provides sections, item counts, and cells.
UICollectionViewDelegate Handles selection and other item interactions.
UICollectionViewLayout Defines the positions and arrangement of items.
UICollectionViewFlowLayout Arranges items in a configurable flow of rows or columns.
Supplementary views Display section headers, footers, or related information.

4. UITableView vs. UICollectionView

Feature UITableView UICollectionView
Default presentation Single-column rows. Items arranged by a layout.
Layout flexibility Best suited to row-based lists. Supports grids, carousels, and custom layouts.
Cell class UITableViewCell UICollectionViewCell
Data source methods Uses row counts and row cell configuration. Uses item counts and item cell configuration.
Layout configuration Uses row heights, separators, and table styles. Uses a collection view layout object.
Typical examples Messages, contacts, settings. Photos, products, card grids.

Both components support reusable cells, scrolling, sections, selection, and dynamic updates. Choose based on the desired presentation rather than assuming one is always faster or better.

5. Understanding Data Sources and Delegates

UIKit separates data management from presentation. A data source supplies the content that a list or collection displays, while a delegate responds to interactions and presentation-related events.

UITableViewDataSource

Common methods include:

  • numberOfSections(in:) — returns the number of sections.
  • tableView(_:numberOfRowsInSection:) — returns the number of rows in a section.
  • tableView(_:cellForRowAt:) — configures and returns a reusable cell.

UITableViewDelegate

Common responsibilities include responding to row selection, configuring row heights, and handling display events.

UICollectionViewDataSource

Common methods include:

  • numberOfSections(in:) — returns the number of sections.
  • collectionView(_:numberOfItemsInSection:) — returns the number of items in a section.
  • collectionView(_:cellForItemAt:) — configures and returns a reusable cell.

UICollectionViewDelegate

Common responsibilities include handling item selection and coordinating interactions with collection view cells.

6. Creating a UITableView in a UIKit Project

A table view can be added through a storyboard or created programmatically. The following example creates a simple list using Swift and UIKit.

Step 1: Create a View Controller

import UIKit

final class FruitListViewController: UIViewController,
                                     UITableViewDataSource,
                                     UITableViewDelegate {
    private let tableView = UITableView(
        frame: .zero,
        style: .plain
    )

    private let fruits = [
        "Apple",
        "Banana",
        "Orange",
        "Mango",
        "Grapes"
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Fruits"
        view.backgroundColor = .systemBackground

        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(
            UITableViewCell.self,
            forCellReuseIdentifier: "FruitCell"
        )

        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(
                equalTo: view.safeAreaLayoutGuide.topAnchor
            ),
            tableView.leadingAnchor.constraint(
                equalTo: view.leadingAnchor
            ),
            tableView.trailingAnchor.constraint(
                equalTo: view.trailingAnchor
            ),
            tableView.bottomAnchor.constraint(
                equalTo: view.bottomAnchor
            )
        ])
    }

    func tableView(
        _ tableView: UITableView,
        numberOfRowsInSection section: Int
    ) -> Int {
        return fruits.count
    }

    func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "FruitCell",
            for: indexPath
        )

        var content = cell.defaultContentConfiguration()
        content.text = fruits[indexPath.row]
        cell.contentConfiguration = content

        return cell
    }

    func tableView(
        _ tableView: UITableView,
        didSelectRowAt indexPath: IndexPath
    ) {
        let fruit = fruits[indexPath.row]
        print("Selected fruit: \(fruit)")
        tableView.deselectRow(
            at: indexPath,
            animated: true
        )
    }
}

Explanation

  1. The view controller creates a table view and an array of fruit names.
  2. The table view's data source and delegate are assigned to the controller.
  3. A cell class is registered with a reuse identifier.
  4. The data source reports the number of rows and configures each cell.
  5. The delegate handles row selection.

This example assumes the view controller is presented by the app's navigation or view-controller setup. The table view itself can be used without a navigation controller.

7. Understanding Cell Reuse in UITableView

Table views reuse cell objects as rows move offscreen. Instead of allocating a new cell for every row, UIKit can recycle an existing cell and configure it for new data. This reduces memory use and improves scrolling performance.

The method dequeueReusableCell(withIdentifier:for:) retrieves a reusable cell for the requested index path. Register the cell class or nib before dequeuing it.

Best Practices for Cell Reuse

  • Use stable, meaningful reuse identifiers.
  • Configure all relevant cell content each time a cell is requested.
  • Reset old asynchronous image or loading state when necessary.
  • Avoid expensive processing inside the cell configuration method.
  • Do not assume a cell permanently represents the same data item.

8. Creating a UICollectionView

A collection view requires a layout that defines how items are arranged. The following example creates a simple two-column grid using UICollectionViewFlowLayout.

Step 1: Configure the Collection View

import UIKit

final class FruitGridViewController: UIViewController,
                                     UICollectionViewDataSource,
                                     UICollectionViewDelegate {
    private var collectionView: UICollectionView!

    private let fruits = [
        "Apple",
        "Banana",
        "Orange",
        "Mango",
        "Grapes",
        "Peach"
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Fruit Gallery"
        view.backgroundColor = .systemBackground

        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 8
        layout.minimumLineSpacing = 8
        layout.sectionInset = UIEdgeInsets(
            top: 12,
            left: 12,
            bottom: 12,
            right: 12
        )

        collectionView = UICollectionView(
            frame: .zero,
            collectionViewLayout: layout
        )

        collectionView.backgroundColor = .systemBackground
        collectionView.dataSource = self
        collectionView.delegate = self

        collectionView.register(
            UICollectionViewCell.self,
            forCellWithReuseIdentifier: "FruitCell"
        )

        view.addSubview(collectionView)
        collectionView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(
                equalTo: view.safeAreaLayoutGuide.topAnchor
            ),
            collectionView.leadingAnchor.constraint(
                equalTo: view.leadingAnchor
            ),
            collectionView.trailingAnchor.constraint(
                equalTo: view.trailingAnchor
            ),
            collectionView.bottomAnchor.constraint(
                equalTo: view.bottomAnchor
            )
        ])
    }

    func collectionView(
        _ collectionView: UICollectionView,
        numberOfItemsInSection section: Int
    ) -> Int {
        return fruits.count
    }

    func collectionView(
        _ collectionView: UICollectionView,
        cellForItemAt indexPath: IndexPath
    ) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: "FruitCell",
            for: indexPath
        )

        var label = cell.contentView.viewWithTag(100)
            as? UILabel

        if label == nil {
            let newLabel = UILabel()
            newLabel.tag = 100
            newLabel.textAlignment = .center
            newLabel.numberOfLines = 0
            newLabel.translatesAutoresizingMaskIntoConstraints = false

            cell.contentView.addSubview(newLabel)

            NSLayoutConstraint.activate([
                newLabel.topAnchor.constraint(
                    equalTo: cell.contentView.topAnchor,
                    constant: 8
                ),
                newLabel.leadingAnchor.constraint(
                    equalTo: cell.contentView.leadingAnchor,
                    constant: 4
                ),
                newLabel.trailingAnchor.constraint(
                    equalTo: cell.contentView.trailingAnchor,
                    constant: -4
                ),
                newLabel.bottomAnchor.constraint(
                    equalTo: cell.contentView.bottomAnchor,
                    constant: -8
                )
            ])

            label = newLabel
        }

        label?.text = fruits[indexPath.item]
        cell.contentView.backgroundColor = .secondarySystemBackground
        cell.contentView.layer.cornerRadius = 10

        return cell
    }

    func collectionView(
        _ collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAt indexPath: IndexPath
    ) -> CGSize {
        let spacing: CGFloat = 8
        let inset: CGFloat = 12
        let availableWidth = collectionView.bounds.width
            - (inset * 2)
            - spacing

        return CGSize(
            width: floor(availableWidth / 2),
            height: 100
        )
    }

    func collectionView(
        _ collectionView: UICollectionView,
        didSelectItemAt indexPath: IndexPath
    ) {
        print("Selected fruit: \(fruits[indexPath.item])")
    }
}

extension FruitGridViewController:
    UICollectionViewDelegateFlowLayout {}

Important: This example uses a basic reusable cell to demonstrate the layout and data source. For a production app, create a custom UICollectionViewCell subclass or use a cell registration so the cell's views are configured cleanly and only once.

9. Understanding UICollectionView Layouts

The layout object determines the size and position of collection view cells and supplementary views. It is separate from the data source, which supplies the actual content.

UICollectionViewFlowLayout

UICollectionViewFlowLayout arranges items in a flowing sequence. Items fill a row or column and wrap to the next line when space is limited.

Important properties include:

  • itemSize — the default size of items.
  • minimumLineSpacing — the minimum spacing between rows or columns.
  • minimumInteritemSpacing — the minimum spacing between items in a line.
  • sectionInset — spacing around the content of a section.
  • scrollDirection — sets horizontal or vertical scrolling.
  • headerReferenceSize and footerReferenceSize — specify supplementary header and footer sizes.

Horizontal Carousel Example

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 180, height: 120)
layout.minimumLineSpacing = 12
layout.sectionInset = UIEdgeInsets(
    top: 8,
    left: 16,
    bottom: 8,
    right: 16
)

This layout can be used for featured products, recommended articles, or horizontally scrolling cards.

Compositional Layout

UICollectionViewCompositionalLayout supports more advanced layouts built from sections, groups, and items. It is useful for interfaces that combine a large featured card, horizontal carousels, and standard grids on the same screen.

10. Creating Custom Cells

Default cells are useful for simple content, but custom cells provide better control over design and behavior. A custom cell can contain labels, images, buttons, badges, and other views.

Example Custom Table View Cell

import UIKit

final class ContactTableViewCell: UITableViewCell {
    private let nameLabel = UILabel()
    private let emailLabel = UILabel()

    override init(
        style: UITableViewCell.CellStyle,
        reuseIdentifier: String?
    ) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        nameLabel.font = .preferredFont(forTextStyle: .headline)
        emailLabel.font = .preferredFont(forTextStyle: .subheadline)
        emailLabel.textColor = .secondaryLabel

        let stack = UIStackView(
            arrangedSubviews: [nameLabel, emailLabel]
        )
        stack.axis = .vertical
        stack.spacing = 4
        stack.translatesAutoresizingMaskIntoConstraints = false

        contentView.addSubview(stack)

        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(
                equalTo: contentView.topAnchor,
                constant: 10
            ),
            stack.leadingAnchor.constraint(
                equalTo: contentView.leadingAnchor,
                constant: 16
            ),
            stack.trailingAnchor.constraint(
                equalTo: contentView.trailingAnchor,
                constant: -16
            ),
            stack.bottomAnchor.constraint(
                equalTo: contentView.bottomAnchor,
                constant: -10
            )
        ])
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    func configure(name: String, email: String) {
        nameLabel.text = name
        emailLabel.text = email
    }
}

Register the custom cell class with the table view, dequeue it in the data source, and call configure(name:email:) using the data associated with the requested index path.

11. Displaying Model Objects

Real applications usually display model objects rather than hard-coded strings. A model groups related properties into a meaningful data structure.

struct Contact {
    let id: UUID
    var name: String
    var email: String
}

let contacts = [
    Contact(
        id: UUID(),
        name: "Aarav Sharma",
        email: "[email protected]"
    ),
    Contact(
        id: UUID(),
        name: "Maya Patel",
        email: "[email protected]"
    )
]

In a table view, the data source can use contacts[indexPath.row] to configure the cell. In a collection view, it can use contacts[indexPath.item]. A stable ID helps identify an item even when the array is sorted or modified.

12. Handling Selection and User Interaction

Table and collection views provide delegate methods for responding when a user selects an item. Selection can navigate to a detail screen, display a preview, or trigger another action.

Table View Selection

func tableView(
    _ tableView: UITableView,
    didSelectRowAt indexPath: IndexPath
) {
    let selectedItem = fruits[indexPath.row]
    print("Selected: \(selectedItem)")
    tableView.deselectRow(at: indexPath, animated: true)
}

Collection View Selection

func collectionView(
    _ collectionView: UICollectionView,
    didSelectItemAt indexPath: IndexPath
) {
    let selectedItem = fruits[indexPath.item]
    print("Selected: \(selectedItem)")
}

For navigation, pass the selected model object or its stable identifier to the destination view controller. Avoid relying on a row number as a permanent identity because sorting, insertion, and deletion can change index paths.

13. Adding and Deleting Rows or Items

When the data changes, the visible list or collection must be updated to match the underlying data source. Update the model first, then update the UI consistently.

Insert a Table View Row

fruits.insert("Pineapple", at: 0)

tableView.insertRows(
    at: [IndexPath(row: 0, section: 0)],
    with: .automatic
)

This example assumes the data source is backed by the same mutable array and the insertion index is valid. The number of rows reported by the data source must agree with the table view's updates.

Delete a Table View Row

let indexPath = IndexPath(row: 0, section: 0)
fruits.remove(at: indexPath.row)

tableView.deleteRows(
    at: [indexPath],
    with: .automatic
)

The array in this example must be declared with var, not let, because it is modified. In a full implementation, confirm the index path is valid and update the data model before calling the corresponding table view update.

Reloading Data

tableView.reloadData()
collectionView.reloadData()

reloadData() refreshes the displayed content from the current data source. It is convenient for simple cases, but targeted insertions, deletions, or diffable data source snapshots can provide smoother updates and clearer state management.

14. Using Diffable Data Sources

Diffable data sources provide an identifier-based way to update list and collection interfaces. Instead of manually coordinating every insertion, deletion, and movement, the app creates a snapshot representing the desired sections and items and applies it to the data source.

Both UITableViewDiffableDataSource and UICollectionViewDiffableDataSource support this pattern. Use unique, stable item identifiers and ensure that the snapshot represents the correct current state of the data.

Conceptual Workflow

  1. Create a diffable data source and provide a cell provider.
  2. Create a snapshot for the section and item identifier types.
  3. Append the sections to the snapshot.
  4. Append the item identifiers to the relevant section.
  5. Apply the snapshot to display the updated state.

Diffable data sources are particularly helpful when data changes frequently, when items move between sections, or when the app needs animated updates without manually managing index-path differences.

15. Section Headers and Footers

Sections group related rows or items. For example, a contacts list may group people by category, while a product gallery may separate products by department.

  • For a table view, use the relevant data source methods or header and footer views to provide section labels.
  • For a collection view, register and provide supplementary views such as headers and footers, and configure the layout to allocate space for them.
  • Use clear section names and maintain a logical grouping structure.

16. Loading Images and Remote Data

Collection views and table views often display images loaded from disk or a remote server. Image loading should not block the main thread.

  • Use asynchronous networking for remote image requests.
  • Cache downloaded images where appropriate.
  • Display a placeholder while an image loads.
  • Account for cell reuse so an old image does not appear in the wrong row or item.
  • Cancel or ignore obsolete requests when a cell is reused.
  • Use prefetching when early data preparation can improve scrolling performance.

For production apps, use a well-designed image-loading layer or an appropriate caching library rather than placing all networking logic directly inside cell configuration methods.

17. Performance Optimization

  • Reuse cells instead of allocating new cells for every item.
  • Keep cell configuration fast and predictable.
  • Use stable identifiers when managing changing data.
  • Prefer targeted updates or diffable data source snapshots for suitable use cases.
  • Use self-sizing cells carefully and provide appropriate layout constraints.
  • Load images and other expensive content asynchronously.
  • Use prefetching for data that is expensive to prepare.
  • Measure scrolling performance with Instruments and test on realistic devices and data volumes.

18. Accessibility and Dynamic Type

Accessible list and grid interfaces allow more people to use an application effectively.

  • Use readable text sizes and support Dynamic Type where appropriate.
  • Give meaningful accessibility labels to icon-only buttons.
  • Ensure cells and controls have suitable accessibility traits.
  • Do not communicate status only through color; include text or another clear indicator.
  • Provide sufficient contrast and comfortable touch targets.
  • Test with VoiceOver and larger accessibility text sizes.

19. Common Errors and Troubleshooting

Problem Possible Cause Solution
Cells do not appear. Incorrect constraints, frame, or data source configuration. Verify layout constraints, registration, and data source assignments.
App crashes while dequeuing a cell. Missing registration or incorrect reuse identifier. Register the correct cell class or nib and use the matching identifier.
Wrong data appears in a cell. Cell content was not fully reconfigured after reuse. Assign every relevant content property when configuring the cell.
Row counts become inconsistent. The model and table updates are out of sync. Update the data source consistently with row insertions and deletions.
Grid items have unexpected sizes. Incorrect layout configuration or item-size calculation. Check section insets, spacing, available width, and layout invalidation.
Scrolling stutters. Expensive cell configuration or synchronous image loading. Move expensive work off the main thread and optimize cell setup.
Selection opens the wrong detail screen. The code relies on an outdated index path. Resolve the selected item's stable identifier from the current model.

20. Practical Project: Contacts and Photo Gallery

Build a small application with two screens to practice both components.

Screen 1: Contacts List

  • Display contacts in a UITableView.
  • Show the contact's name and email.
  • Open a detail screen when a row is selected.
  • Add, edit, and delete contacts.
  • Display a helpful empty state when no contacts exist.

Screen 2: Photo Gallery

  • Display images in a UICollectionView.
  • Use a grid layout with suitable spacing.
  • Open a larger preview when an image is selected.
  • Support loading placeholders and asynchronous image retrieval.
  • Experiment with a horizontal carousel layout.

Suggested Development Plan

  1. Create model structures for contacts and gallery items.
  2. Implement the table view data source and delegate.
  3. Implement a reusable custom table view cell.
  4. Implement the collection view and its layout.
  5. Add navigation and selection handling.
  6. Implement model updates and UI refreshes.
  7. Test empty, large, and changing datasets.
  8. Check accessibility and scrolling performance.

21. Best Practices

  • Choose UITableView for straightforward row-based content and UICollectionView when layout flexibility is needed.
  • Separate data models from view controllers where practical.
  • Keep cell configuration reusable and focused.
  • Use stable item identifiers for changing datasets.
  • Update the model and visible UI consistently.
  • Handle asynchronous image loading safely with cell reuse.
  • Prefer appropriate modern APIs, including diffable data sources, for complex updates.
  • Test selection, deletion, accessibility, and dynamic content changes.

22. Interview Questions and Answers

Q1. What is UITableView?

UITableView is a UIKit component that displays data in a single column of reusable rows, optionally grouped into sections.

Q2. What is UICollectionView?

UICollectionView displays items using a configurable layout, supporting grids, carousels, and custom arrangements.

Q3. What is the role of a data source?

The data source provides the number of sections and items or rows and configures the cells that display the data.

Q4. What is the difference between a data source and a delegate?

The data source supplies content, while the delegate handles interactions and other behavior such as selection and layout-related events.

Q5. Why are reusable cells important?

Reusable cells reduce unnecessary view creation, helping manage memory and maintain smooth scrolling.

Q6. What is IndexPath?

An IndexPath identifies an item's position. In a table view it commonly represents a section and row; in a collection view it represents a section and item.

Q7. What is UICollectionViewFlowLayout?

It is a layout class that arranges collection view items in rows or columns with configurable item sizes, spacing, and section insets.

Q8. When should a collection view be preferred?

Use it when the interface requires a grid, horizontal carousel, or other flexible layout that is difficult to represent with a standard table view.

Q9. What is a diffable data source?

It manages list or collection updates using unique item identifiers and snapshots describing the desired UI state.

Q10. How can list scrolling performance be improved?

Reuse cells, keep configuration fast, load images asynchronously, use suitable layouts, and measure performance with development tools.

23. Practice Exercises

  1. Create a table view displaying a list of books with titles and authors.
  2. Add section headers to group books by category.
  3. Create a collection view showing product cards in a two-column grid.
  4. Change the collection view to a horizontal carousel.
  5. Implement selection and navigate to a detail screen.
  6. Add a delete action to a table view and keep the data source consistent.
  7. Build a custom cell containing an image, title, and subtitle.
  8. Replace manual updates with a diffable data source.
  9. Test the screens with large text settings and VoiceOver.

24. Quick Revision Checklist

  • UITableView displays single-column rows.
  • UICollectionView displays items using a configurable layout.
  • Data sources provide content and configure reusable cells.
  • Delegates handle selection and related interactions.
  • Cells should be reused and configured consistently.
  • Collection view layouts determine item positions and sizes.
  • Stable identifiers and consistent model updates help prevent UI errors.
  • Asynchronous loading, prefetching, and efficient configuration improve performance.

25. Conclusion

UITableView and UICollectionView are fundamental UIKit tools for creating data-driven iOS interfaces. Table views are well suited to structured row-based lists, while collection views provide greater flexibility for grids, carousels, and custom arrangements. Understanding data sources, delegates, reusable cells, layouts, and update strategies helps developers create interfaces that are responsive, maintainable, and accessible.

Continue learning with JustAcademy's iOS App Development Course, register for a course demo, or visit the JustAcademy homepage.

Previous Next
whatsapp