Unit 4: TableViews

INT372 — Iphone Application Programming 8 min read

I. Orientation — The Table-Based Interface

A table view is a UIKit control for displaying vertically scrollable data as rows, optionally divided into sections. UITableView follows a data-source and delegate architecture: the data source supplies content, while the delegate manages appearance, selection, and interaction.

  • Defining properties:
    • Row-based presentation: Each item is represented by a UITableViewCell.
    • Section organization: Rows may be divided into numbered or named sections.
    • Cell reuse: Off-screen cells are recycled through reuse identifiers, reducing memory use.
    • Data source: An object conforming to UITableViewDataSource reports sections, rows, and cells.
    • Delegate: An object conforming to UITableViewDelegate responds to selection and controls presentation details.
    • Index paths: IndexPath identifies a row through its section and row properties.
    • Controller integration: A table can be managed by UITableViewController or embedded in an ordinary UIViewController.
    • Common styles:
    • .plain presents continuous sections.
    • .grouped visually separates sections into groups.
    • .insetGrouped presents inset groups on supported iOS versions.
    • Main-thread rule: Interface changes, including reloadData(), must occur on the main thread.

II. UITableView Architecture — Core Roles and Lifecycle

A. UITableView basics

UITableView displays data by repeatedly asking its data source how many rows exist and how each visible cell should be configured.

  • Essential objects:
    • Table view: UITableView owns scrolling, layout, selection, and cell reuse.
    • Cell: UITableViewCell renders one row and may contain text, images, accessories, or custom controls.
    • Data model: An array or another collection stores the actual application data; the table view is only its visual representation.
  • Required data-source methods:
    • Row count: tableView(_:numberOfRowsInSection:) returns the number of model items in a section.
    • Cell creation: tableView(_:cellForRowAt:) dequeues and configures a cell for an IndexPath.
  • Optional section method: numberOfSections(in:) defaults to one when it is not implemented.
  • Reuse process:
    1. Register a cell class or prototype with a reuse identifier such as "ItemCell".
    2. Dequeue a reusable cell for the requested index path.
    3. Replace all reusable content with values from the current model item.
  • Registration example:
SWIFT
tableView.register(
    UITableViewCell.self,
    forCellReuseIdentifier: "ItemCell"
)
  • Configuration principle: A reused cell may contain old state, so text, images, accessory types, colors, and control values must all be reset in cellForRowAt.
  • Refreshing content: tableView.reloadData() asks the data source for the complete structure again; targeted insertion, deletion, or reloading is preferable for small animated changes.
  • Sizing options: rowHeight assigns a fixed height, while UITableView.automaticDimension supports self-sizing cells whose constraints fully define vertical layout.

III. Basic Construction — Creating and Populating Rows

A. Implementing simple table

A simple table connects an array-backed model to one table section through the required data-source methods.

  • Model: The array index corresponds directly to indexPath.row; for example, row 0 displays "Phone".
  • Controller setup: UITableViewController supplies a full-screen table and already exposes it through its tableView property.
  • Implementation:
SWIFT
final class ItemsViewController: UITableViewController {
    private let items = ["Phone", "Tablet", "Watch"]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(
            UITableViewCell.self,
            forCellReuseIdentifier: "ItemCell"
        )
    }

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

    override func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "ItemCell",
            for: indexPath
        )
        cell.textLabel?.text = items[indexPath.row]
        return cell
    }
}
  • Index-path mapping: items[indexPath.row] is valid because the reported row count equals items.count.
  • Embedded alternative: In a standard UIViewController, create an outlet or property for the table, set dataSource and delegate, and constrain the table to its container.
  • Consistency requirement: After changing the model, update the table accordingly; reporting more rows than the array contains causes an out-of-range failure.
  • Storyboard prototypes: A prototype cell can carry the identifier "ItemCell", removing the need for class registration when the storyboard constructs the table.

IV. Cell Presentation — Standard and Custom Content

A. Customizing TableView cells

Cell customization ranges from configuring built-in styles to creating a subclass with a reusable, constraint-based layout.

  • Standard content:
    • Primary text: textLabel shows the main value.
    • Secondary text: detailTextLabel is available in styles such as .subtitle.
    • Image: imageView displays a leading image.
    • Accessory: .disclosureIndicator, .checkmark, or .detailButton communicates an available action or state.
  • Standard-style example:
SWIFT
let cell = UITableViewCell(
    style: .subtitle,
    reuseIdentifier: "ProductCell"
)
cell.textLabel?.text = "iPhone"
cell.detailTextLabel?.text = "In stock"
cell.accessoryType = .disclosureIndicator
  • Custom subclass: Define views once in init(style:reuseIdentifier:), add them to contentView, and activate Auto Layout constraints. Configure only model-dependent values when the cell is reused.
  • Type-safe dequeueing:
SWIFT
guard let cell = tableView.dequeueReusableCell(
    withIdentifier: ProductCell.reuseIdentifier,
    for: indexPath
) as? ProductCell else {
    fatalError("ProductCell is not registered")
}
cell.configure(with: products[indexPath.row])
  • Reuse preparation: Override prepareForReuse() to cancel pending image requests and clear temporary state that configuration may not immediately replace.
  • Dynamic height: Labels with numberOfLines = 0 and complete top-to-bottom constraints allow automaticDimension to calculate row height.
  • Performance: Avoid expensive synchronous image loading in cellForRowAt; cache images and update only the cell still representing the intended model item.

V. Sectioned Tables — Organization and Fast Access

A. Grouped and indexed sections

Grouped sections visually classify related rows, while an index provides direct navigation among many alphabetically or logically ordered sections.

  1. Grouped sections:

    • Data structure: A nested collection represents sections and rows, such as sections[section].items[row].
    • Section count: numberOfSections(in:) returns sections.count.
    • Row count: tableView(_:numberOfRowsInSection:) returns sections[section].items.count.
    • Style selection: Construct the table with .grouped or initialize a controller using UITableViewController(style: .grouped).
  2. Indexed sections:

    • Index titles: sectionIndexTitles(for:) returns short labels such as ["A", "B", "C"].
    • Mapping: tableView(_:sectionForSectionIndexTitle:at:) maps the selected index title to a section number.
    • Ordering condition: The index and section model must use the same stable ordering.
SWIFT
override func numberOfSections(
    in tableView: UITableView
) -> Int {
    sections.count
}

override func tableView(
    _ tableView: UITableView,
    numberOfRowsInSection section: Int
) -> Int {
    sections[section].items.count
}

override func sectionIndexTitles(
    for tableView: UITableView
) -> [String]? {
    sections.map(\.title)
}
  • Comparison: Grouping communicates relationships through spacing and headings; indexing optimizes movement through a long table. A contact list can use both alphabetical sections and an A–Z index.

VI. Supplementary Content — Context Around Rows

A. Adding header, footer, and image

Headers and footers describe sections or the whole table, while images may appear inside cells or supplementary views.

  • Section text:
    • Header: tableView(_:titleForHeaderInSection:) returns a section heading.
    • Footer: tableView(_:titleForFooterInSection:) returns explanatory text below a section.
  • Custom section views: tableView(_:viewForHeaderInSection:) can return a configured UITableViewHeaderFooterView; register reusable header/footer classes to avoid repeated allocation.
  • Whole-table content: Assign a view to tableHeaderView or tableFooterView for content that appears once rather than once per section.
  • Image in a standard cell:
SWIFT
cell.imageView?.image = UIImage(named: "phone")
cell.textLabel?.text = products[indexPath.row].name
  • Asset handling: UIImage(named:) loads a named image from the asset catalog and supports scale variants such as 2x and 3x.
  • Remote images: Load asynchronously, cache results, and use a placeholder while waiting. Before assigning a downloaded image, verify that the cell still corresponds to the requested item.
  • Layout distinction: tableHeaderView scrolls with table content; a separate view constrained above the table remains fixed.

VII. Selection — Responding to User Choice

A. Displaying item selected

Selection is normally handled by the table delegate, which receives the chosen row’s index path.

  • Delegate callback: tableView(_:didSelectRowAt:) runs after the user taps a selectable row.
  • Model lookup: Use both indexPath.section and indexPath.row when the model contains sections.
  • Display example:
SWIFT
override func tableView(
    _ tableView: UITableView,
    didSelectRowAt indexPath: IndexPath
) {
    let item = items[indexPath.row]

    let alert = UIAlertController(
        title: "Selected Item",
        message: item,
        preferredStyle: .alert
    )
    alert.addAction(UIAlertAction(title: "OK", style: .default))
    present(alert, animated: true)

    tableView.deselectRow(at: indexPath, animated: true)
}
  • Selection state: indexPathForSelectedRow reports the current selected row when one exists.
  • Visual behavior: Deselecting after handling the tap is conventional when selection performs a transient action or opens another screen.
  • Persistent choices: For settings, store the selected value in the model and render .checkmark accessories from that model rather than relying only on visible cell state.
  • Control conflict: Buttons and switches inside a cell may handle their own events, so their actions should identify the relevant model item independently.

VIII. Screen Transitions — Passing the Selected Model

A. Navigating to another view

A selected row can open a detail view by pushing it onto a navigation stack or presenting it modally.

  • Navigation controller: pushViewController(_:animated:) is appropriate for hierarchical movement from a list to item details.
  • Data transfer: Assign the selected model object to a property on the destination before navigation.
  • Programmatic example:
SWIFT
override func tableView(
    _ tableView: UITableView,
    didSelectRowAt indexPath: IndexPath
) {
    let detail = DetailViewController()
    detail.item = items[indexPath.row]
    navigationController?.pushViewController(
        detail,
        animated: true
    )
}
  • Storyboard segue: Call performSegue(withIdentifier:sender:), then use prepare(for:sender:) to configure the destination.
  • Destination handling: A navigation controller may wrap the real destination, so obtain its top view controller before assigning data when necessary.
  • Dependency principle: Pass the selected model value or stable identifier, not the source cell; cells are reusable presentation objects and may leave the screen.
  • Disclosure cue: A .disclosureIndicator accessory conventionally signals that tapping the row opens another view.
  • State restoration: When returning from an editable detail screen, update the source model and reload the affected row so the table reflects the saved changes.