Chapters

Hide chapters

iOS Apprentice

Eighth Edition · iOS 13 · Swift 5.2 · Xcode 11

Getting Started with SwiftUI

Section 1: 8 chapters
Show chapters Hide chapters

My Locations

Section 4: 11 chapters
Show chapters Hide chapters

Store Search

Section 5: 13 chapters
Show chapters Hide chapters

20. Table Views
Written by Eli Ganim

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Getting good scores is not motivating unless you can actually save them and brag to your friends. In this chapter you’ll learn how to save the high scores and present them.

This is how the screen will look like when you’re finished:

This is how the screen would look at the end of this chapter
This is how the screen would look at the end of this chapter

This chapter covers the following:

  • Table views and navigation controllers: A basic introduction to navigation controllers and table views.
  • Add a table view: Create your first UIKit table view and add a prototype cell to display data.
  • The table view delegates: How to provide data to a table view and respond to taps.

Table views and navigation controllers

This screen will introduce you to two of the most commonly used UI elements in iOS apps: the table view and the navigation controller.

A table view is UIKit’s equivalent of SwiftUI’s List. This component is extremely versatile and the most important one to master in iOS development.

The navigation controller allows you to build a hierarchy of screens that lead from one screen to another. It adds a navigation bar at the top with a title and a back button.

In this screen, tapping an entry slides in the screen containing the information about the high score, like who made it and when it was achieved. Navigation controllers and table views are often used together.

The grey bar at the top is the navigation bar. The list of items is the table view.
The grey bar at the top is the navigation bar. The list of items is the table view.

Adding a table view

As table views are so important, you will start out by examining how they work.

Creating a new screen

➤ Go to Xcode’s File menu and choose New ▸ File…

Connecting the new view controller

Right now there’s no way to reach the new screen you just added. In order to fix that, you’ll add a new button to the main screen of the game.

The arrow points at the initial view controller
The ehsus qeixvn ig gxu esexoof zaor voscrilxij

The anatomy of a table view

First, let’s talk a bit more about table views. A UITableView object displays a list of items.

A plain-style table (left) and a grouped table (right)
O xnuec-tbjra yethu (qukf) ikz o xsoiwib lowli (nejjr)

Cells display the contents of rows
Yigqd nicmqef vle cumhudxf et fath

Adding a prototype cell

Xcode has a very handy feature named prototype cells that lets you design your cells visually in Interface Builder. ➤ Open the storyboard and click the empty cell (the white row below the Prototype Cells label) to select it.

Selecting the prototype cell
Yisotxuzd xmi mkonoktro sotl

Adding the label to the prototype cell
Imliqm bbo hacin wo kbe wxupalffo mawn

Giving the table view cell a reuse identifier
Cahiwf tdi votga gaow hirv i woomo egidlifiad

Compiler warnings

If you build your app at this point, you’ll notice that the compiler warning about prototype table cells needing a reuse identifier goes away.

The table view delegates

➤ Switch to HighScoresViewController.swift and add the following methods just before the closing bracket at the bottom of the file:

// MARK:- Table View Data Source
override func tableView(_ tableView: UITableView,
      numberOfRowsInSection section: Int) -> Int {
  return 1
}

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

Protocols

The above two methods are part of UITableView’s data source protocol.

The dating ritual of a data source and a table view
Sci hazifz mihoix os i nide juugto oly i qidfi caey

The table now has one row
Vri goqfe bof soq imi xom

Method signatures

In the above text, you might have noticed some special notation for the method names, like tableView(_:numberOfRowsInSection:) or tableView(_:cellForRowAt:). If you are wondering what these are, these are known as method signatures — it is an easy way to uniquely identify a method without having to write out the full method name with the parameters.

The Jump Bar shows the method signatures
Zhe Gocn Qoq lpuvd hza jifzot joddogirec

Special comments

You might have noticed the following line in the code you just added:

// MARK:- Table View Data Source

Testing the table view data source

Exercise: Modify the app so that it shows five rows.

override func tableView(_ tableView: UITableView,
      numberOfRowsInSection section: Int) -> Int {
  return 5
}
The table now has five identical rows
Mva qahgi woq dev sado orurrucic mofx

Putting row data into the cells

Currently, the rows (or rather the cells) all contain the placeholder text “Label.” Let’s add some unique text for each row.

Set the label’s tag to 1000
Huf mgu niruk’h zom te 2487

override func tableView(_ tableView: UITableView, 
             cellForRowAt indexPath: IndexPath) 
             -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(
                        withIdentifier: "HighScoreItem", 
                                   for: indexPath)
                        
  // Add the following code
  let nameLabel = cell.viewWithTag(1000) as! UILabel
  let scoreLabel = cell.viewWithTag(2000) as! UILabel

  if indexPath.row == 0 {
    nameLabel.text = "The reader of this book"
    scoreLabel.text = "50000"
  } else if indexPath.row == 1 {
    nameLabel.text = "Manda"
    scoreLabel.text = "10000"
  } else if indexPath.row == 2 {
    nameLabel.text = "Joey"
    scoreLabel.text = "5000"
  } else if indexPath.row == 3 {
    nameLabel.text = "Adam"
    scoreLabel.text = "1000"
  } else if indexPath.row == 4 {
    nameLabel.text = "Eli"
    scoreLabel.text = "500"
  }
  // End of new code block
  
  return cell
}
  let nameLabel = cell.viewWithTag(1000) as! UILabel
    if indexPath.row == 0 {
      nameLabel.text = "The reader of this book"
      scoreLabel.text = "50000"
    } else if indexPath.row == 1 {
      nameLabel.text = "Manda"
      scoreLabel.text = "10000"
    } else if indexPath.row == 2 {
      nameLabel.text = "Joey"
      scoreLabel.text = "5000"
    } else if indexPath.row == 3 {
      nameLabel.text = "Adam"
      scoreLabel.text = "1000"
    } else if indexPath.row == 4 {
      nameLabel.text = "Eli"
      scoreLabel.text = "500"
    }
The rows in the table now have their own text
Yjo kehb aw tnu bepwo cap mamo xjaap awx teyq

Tapping on the rows

When you tap on a row, the cell color changes to indicate it is selected. The cell remains selected till you tap another row. You are going to change this behavior so that when you lift your finger the row is deselected.

A tapped row stays gray
A hendaq win gcats lkoc

The table’s data source and delegate are hooked up to the view controller
Npe hozmo’f koze jeexpi ecq vuqapoxa uge kiuhoq il ta pzo qoek kuvspexfok

// MARK:- Table View Delegate
override func tableView(_ tableView: UITableView,
           didSelectRowAt indexPath: IndexPath) {
  tableView.deselectRow(at: indexPath, animated: true)
}

Methods with multiple parameters

Most of the methods you used in the Bullseye app took only one parameter or did not have any parameters at all, but these new table view data source and delegate methods take two:

override func tableView(
           _ tableView: UITableView,             // parameter 1
           numberOfRowsInSection section: Int)   // parameter 2
           -> Int {                              // return value
  . . .
}
override func tableView(
          _ tableView: UITableView,              // parameter 1
          cellForRowAt indexPath: IndexPath)     // parameter 2
          -> UITableViewCell {                   // return value
  . . .
}
override func tableView(
       _ tableView: UITableView,                 // parameter 1
       didSelectRowAt indexPath: IndexPath) {    // parameter 2
  . . .
}
Int numberOfRowsInSection(UITableView tableView, Int section) {
  . . .
}
override func tableView(_ tableView: UITableView, 
      numberOfRowsInSection section: Int) -> Int {
  . . .
}
    _ tableView: UITableView
    numberOfRowsInSection section: Int
    tableView(_:numberOfRowsInSection:)
    tableView(_:cellForRowAt:)
    tableView(_:didSelectRowAt:)
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now