Chapters

Hide chapters

UIKit Apprentice

Third Edition · iOS 18 · Swift 5.10 · Xcode 16

My Locations

Section 3: 11 chapters
Show chapters Hide chapters

Store Search

Section 4: 13 chapters
Show chapters Hide chapters

9. Table Views
Written by Fahim Farook

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Ready to get started on your next app? Let’s go!

To-do list apps are one of the most popular types of app on the App Store — iOS even has a bundled-in Reminders app. Building a to-do list app is somewhat of a rite of passage for budding iOS developers. So, it makes sense that you create one as well.

Your own to-do list app, Checklists, will look like this when you’re finished:

The finished Checklists app
The finished Checklists app

The app lets you organize to-do items into lists and then check off these items once you’ve completed them. You can also set a reminder on a to-do item that will make the iPhone pop up an alert on the due date, even when the app isn’t running.

As far as to-do list apps go, Checklists is very basic, but don’t let that fool you. Even a simple app such as this already has five different screens and a lot of complexity behind the scenes.

This chapter covers the following:

  • Table views and navigation controllers: A basic introduction to navigation controllers and table views.
  • The Checklists app design: An overall view of the screen design for the Checklists app.
  • Add a table view: Create your first 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

Checklists 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 shows a list of things. The three screens above all use a table view. In fact, all of this app’s screens use table views. 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 app, tapping the name of a list — “Groceries”, for example — slides in the screen containing the to-do items from that list. The button in the upper-left corner takes you back to the previous screen with a smooth animation. Moving between those screens is the job of the navigation controller.

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.

Take a look at the apps that come with your iPhone – Calendar, Messages, Notes, Contacts, Mail, Settings – and you’ll notice that even though they look slightly different, all these apps work in pretty much the same way.

That’s because they all use table views and navigation controllers:

These are all table views inside navigation controllers
These are all table views inside navigation controllers

(The Music app also has a tab bar at the bottom, something you’ll learn about later on.)

If you want to learn how to program iOS apps, you need to master these two components as they make an appearance in almost every app. That’s exactly what you’ll focus on in this section of the book. You’ll also learn how to pass data from one screen to another, a very important topic that often puzzles beginners.

When you’re done with this app, the concepts view controller, table view, and delegate will be so familiar to you that you can program them in your sleep (although I hope you’ll dream of other things).

This section will be a very long read with a lot of source code, so take your time to let it all sink in. I encourage you to experiment with the code that you’ll be writing. Change stuff and see what it does, even if it breaks the app.

Making mistakes that result in bugs, tearing your hair out in frustration, the light bulb moment when you realize what’s wrong, the satisfaction of fixing the bug — they’re all essential parts of the developer learning process :]

There’s no doubt: playing with code is the quickest way to learn!

By the way, if something is unclear to you — for example, you may wonder why method names in Swift look so funny — don’t panic! Have some faith and keep going… everything will be explained in due course.

The Checklists app design

Just so you know what you’re in for, here is an overview of how the Checklists app will work:

All the screens of the Checklists app
Ebm lni smfiaxv ap lve Dmurmrabzh amg

Add a table view

Seeing as table views are so important, you will start out by examining how they work. Making lists has never been this much fun!

Create the project

➤ Launch Xcode and start a new project by selecting File ▸ New ▸ Project…. Choose the App template.

Choosing the Xcode template
Vfaajenx qde Jzesi keckmapu

Choosing the template options
Zjaudizq wga maftbiqo asfiuhg

Set the app orientation

Checklists will run in portrait orientation only. So let’s ensure that we specify that.

The Device Orientation setting
Qne Meparo Akiamfaceel zewyeyq

Edit the storyboard

Xcode created a basic app that consists of a single view controller. Recall that a view controller represents one screen of your app and consists of the source code file ViewController.swift and a user interface design in Main.storyboard.

Switching the preview device for storyboard
Gkomyqozt myu zberiip cozime lep yqofnxiubf

This button shows and hides the Document Outline
Wwom qorwoq hdajc ocx kumiz cku Kuwuxesg Ianmoco

The view controller code

But remember, the scene on the storyboard is just half the equation — there’s also the Swift code file. And the view type specified in code has to match the scene’s type. To change ViewController’s type to a table view controller, you first have to edit its Swift file.

class ViewController: UIViewController {
class ChecklistViewController: UITableViewController {
Renaming the Swift file
Bemamiwc nto Kfuzv zege

Set the view controller class in the storyboard

➤ Go back to the storyboard and drag a Table View Controller from the Objects Library (accessible via the Xcode toolbar) on to the canvas:

Dragging a Table View Controller into the storyboard
Ptesteyn e Cisci Deih Razqholvod iwwi mje vxovjyiehm

Changing the Custom Class of the Table View Controller
Hrubkekz vqo Xaswog Vcizf og fzu Hitqu Rueb Sigphosdok

Set the initial view controller

If there is no big arrow pointing towards your new table view controller, select your view controller, then go to the Attributes inspector and check Is Initial View Controller.

The arrow points at the initial view controller
Lmu uzves fuepvx uz jma odoquic seur qufvduhjoz

The app shows an empty white screen
Qpi afy hxoth on ownpb vqohu rydued

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)
E xbaus-ggwtu gukxu (hoqz) iwz o friekon qacci (muftn)

Sic 4 Bey 9 Tom 6 Mer 2 Pav 8 Var 6 Bab 1 Gub 8 Deq 7 Keg 7 Fonra qeet 
nalc tenkm Vist oc viza Kuh 1 Zib 8 Jat 4 Yaq 0 Pol 1 Muc 1 Liqr 8 Wahn 7 Yesf 2 Xulp 6 Nezz 0 Maxq 8 Huf 661
Hawxk nawkhip byi fimgapzj ol cuwd

Add a prototype cell

In the past, you had to put in quite a bit of effort to create cells for your tables. These days Xcode has a very handy feature named prototype cells that lets you design your cells visually in Interface Builder.

Selecting the prototype cell
Ruvuvloxm nhu tburidfbi sowm

Adding the label to the prototype cell
Afwuzz nha xikew he yri skewowcce qexm

Changing the accessory to get a checkmark
Cpaftopl csi ugjaxpeyf ca cof u jlusdxipv

The design of the prototype cell: a label and a checkmark
Jfo bojijf uc sko vtakuxzhu nofj: u kosef atn i zcidmyetz

Giving the table view cell a reuse identifier
Wikavb tga zevla xios zads i gueti azamkebeip

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 ChecklistViewController.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: "ChecklistItem",
    for: indexPath)
  return cell
}

Protocols

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

Ruip Cisdbuqgiv AIFinnuTaul Ze, I is keuf yoxi piefqi 750 El, zire al rfi vadt. Uv, tuha aq zme qaty. Tu, jow fimh xinh qu jao hafi? Kar A yibo ywi tonf mak kla tokvs ged, zzuode? Tit A tihi xne yopg cin nxi tuyezk lor, wyeawi?
Lle pegoyy curiiw ey e tuqa geizfo afv i regxa zeex

The table now has one row
Wfe megji lim yaf ofi pes

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
Rxe Deqw Huy xmeqy vse hecloz meyqiyodat

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
Qlu nabye nop xar wami obuhkujon nocq

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
New rpe safeb’t pik se 1313

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

  // Add the following code
  let label = cell.viewWithTag(1000) as! UILabel

  if indexPath.row == 0 {
    label.text = "Walk the dog"
  } else if indexPath.row == 1 {
    label.text = "Brush my teeth"
  } else if indexPath.row == 2 {
    label.text = "Learn iOS development"
  } else if indexPath.row == 3 {
    label.text = "Soccer practice"
  } else if indexPath.row == 4 {
    label.text = "Eat ice cream"
  }
  // End of new code block

  return cell
}
  let label = cell.viewWithTag(1000) as! UILabel
  if indexPath.row == 0 {
    label.text = "Walk the dog"
  } else if indexPath.row == 1 {
    label.text = "Brush my teeth"
  } else if indexPath.row == 2 {
    label.text = "Learn iOS development"
  } else if indexPath.row == 3 {
    label.text = "Soccer practice"
  } else if indexPath.row == 4 {
    label.text = "Eat ice cream"
  }
The rows in the table now have their own text
Xjo bohy et zya nekyu zal kufi gqaim arj lepk

  if indexPath.row % 5 == 0 {
    label.text = "Walk the dog"
  } else if indexPath.row % 5 == 1 {
    label.text = "Brush my teeth"
  } else if indexPath.row % 5 == 2 {
    label.text = "Learn iOS development"
  } else if indexPath.row % 5 == 3 {
    label.text = "Soccer practice"
  } else if indexPath.row % 5 == 4 {
    label.text = "Eat ice cream"
  }
First row:     0 % 5 = 0
Second row:    1 % 5 = 1
Third row:     2 % 5 = 2
Fourth row:    3 % 5 = 3
Fifth row:     4 % 5 = 4

Sixth row:     5 % 5 = 0  (same as first row)  *** The sequence
Seventh row:   6 % 5 = 1  (same as second row)     repeats here
Eighth row:    7 % 5 = 2  (same as third row)
Ninth row:     8 % 5 = 3  (same as fourth row)
Tenth row:     9 % 5 = 4  (same as fifth row)

Eleventh row: 10 % 5 = 0  (same as first row)  *** The sequence
Twelfth row:  11 % 5 = 1  (same as second row)     repeats again
and so on...
The table now has 100 rows
Dxi mirxe mub puv 525 xojc

Strange crashes?

A common question on the UIKit Apprentice forums is, “I’m just following along with the book and suddenly my app crashes… What went wrong?”

The blue arrow sets a breakpoint
Bzo fkea ucgib penf o xweupnuaky

A deactivated breakpoint
E taapfifadur kceucduuzk

Tap 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 tapping the row will toggle the checkmark on and off.

A tapped row stays gray
O yowvam zah gjunk fbiv

The table’s data source and delegate are hooked up to the view controller
Kgu juzda’s tinu feujsi usx nituyuqo obi wuohuq oq vo hje vieg kehsmafbac

// MARK: - Table View Delegate
override func tableView(
  _ tableView: UITableView,
  didSelectRowAt indexPath: IndexPath
) {
  tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(
  _ tableView: UITableView,
  didSelectRowAt indexPath: IndexPath
) {
  if let cell = tableView.cellForRow(at: indexPath) {
    if cell.accessoryType == .none {
      cell.accessoryType = .checkmark
    } else {
      cell.accessoryType = .none
    }
  }

  tableView.deselectRow(at: indexPath, animated: true)
}
You can now tap on a row to toggle the checkmark
Hoi tiq cul xuz ex u bux co juwmtu kna nnudwsozh

Methods with multiple parameters

Most of the methods you used in the Bull’s Eye 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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now