Discover how to successfully build a dynamic list of shows in iOS with this tutorial guiding you through the steps of connecting a class to the storyboard, adding properties, editing the datasource protocol methods, and crafting code to populate the table.
This exercise is excerpted from Noble Desktop’s past app development training materials and is compatible with iOS updates through 2021. To learn current skills in web development, check out our coding bootcamps in NYC and live online.
Topics covered in this iOS Development tutorial:
Connecting a class to the storyboard, Adding properties, Editing the datasource protocol methods
Exercise Preview
Exercise Overview
The list of upcoming shows we built in the previous exercise is looking good, but we really need these cells to be created dynamically so they are easier to update when we need to add more shows. In this exercise, we will write code to populate the table instead of manually setting each cell in the Editor.
Getting Started
-
If you completed the previous exercise you can skip the following sidebar. We recommend you finish the previous exercise (1B) before starting this one.
If you completed the previous exercise, Jive Factory.xcodeproj should still be open. If you closed it, re-open it (from yourname-iOS Dev Level 2 Class > Jive Factory).
If You Did Not Complete the Previous Exercise (1B)
- Close any files you may have open and switch to the Desktop.
- Navigate to Class Files > yourname-iOS Dev Level 2 Class.
- Duplicate the Jive Factory Ready for Table View Controller folder.
- Rename the folder to Jive Factory.
- Open Jive Factory > Jive Factory.xcodeproj.
-
We want to keep the look and feel of the cells we built in the previous exercise. We’ll use the first cell as a template, but we don’t need the rest of them.
In the Document Outline, click the second Ambulance LTD cell to select it.
- Shift–click on the last Black Angels cell to select the two others.
- Hit Delete.
- In the Document Outline, click Table View to select it.
- In the Attributes inspector
, from the Content menu, choose Dynamic Prototypes.
- In the Document Outline, select Table View Cell.
In the Attributes inspector
, next to Identifier, type bandCell and hit Return. This defines a name we can use to programmatically refer to later.
Creating a Class
We need to create a new class that we can customize to populate the cells dynamically.
- In the Project navigator, select ViewController.swift. (We want the file to be added after this file, that’s why we had you select it.)
- Go to File > New > File.
- Under iOS and Source, double–click on the Cocoa Touch Class template.
- From the Subclass of menu, choose UITableViewController. (Be careful to choose the UITable ViewController, not the UIViewController!)
-
Edit the name of the Class to be: BandsTableViewController
NOTE: UITableViewController is the object we are currently using on the storyboard. By making our class a subclass of the UITableViewController, it will have all the functionality it currently has, plus any additional functionality we add in the code to this new BandsTableViewController class.
- Click Next.
- You should already be in the Jive Factory folder, so click Create.
Notice BandsTableViewController.swift has been added in the Project navigator. Now we have a class we can work with.
Connecting the Class to the Storyboard
Now we need to connect our Table View Controller to the class we just created.
- In the Project navigator, click on Main.
- In the Document Outline, click Table View. (You might have to open Table View Controller Scene and Table View Controller to see it.)
- In the Utilities area on the right, click on the Connections inspector tab
.
-
Notice there are two outlets that are assigned to the Table View Controller: dataSource and delegate.
The dataSource and delegate are two protocols that need to be defined in order to populate a Table View. Protocols are agreements between one class and another that a class is going to conform to or implement certain methods. For example, the dataSource protocol defines methods that will provide data to our table view. When you create a table view controller by dragging it from the Object library as we did in the previous exercise, it defaults to itself being the dataSource and delegate for the table view. Once we connect our new class to this view, the new class will be used as the dataSource and delegate and therefore we can control the flow of data to the table view.
- Let’s connect the view we have in the storyboard to our new class. In the Document Outline, click Table View Controller.
- In the Utilities area on the right, click the Identity inspector tab
.
-
Next to Class, type B and Xcode should autocomplete to: BandsTableViewController
NOTE: Sometimes Xcode takes a while to recognize a new class, so if you do not see any autocomplete suggestions, you will just have to type it in manually.
- Hit Return to apply the change.
- In the Document Outline, click Table View.
- In the Utilities area on the right, click the Connections inspector tab
.
- Notice that dataSource and delegate are now connected to the Bands Table View Controller. Awesome! Now we can start adding the code we need to populate the cells.
Go to File > Save or hit Cmd–S.
Adding Properties for the Band Data
Before we can draw the table and all the cells, we need to represent the data (title, subtitle, image) for all the bands in the code. Let’s start by creating properties for the Title, Subtitle, and Image.
- In the Project navigator, click on BandsTableViewController.swift to open it.
-
Near the top of the code, add the following array for the band titles:
class BandsTableViewController: UITableViewController { let bandTitles = ["Nicole Atkins", "Ambulance LTD", "Sleepies", "Black Angels"] override func viewDidLoad() {
Recall that an array is a type of data collection used to hold a list of items. You will notice that this array is a constant, which means once you have set the values for this array, the data cannot be changed. It’s also worth noting that arrays must contain all one type of data. In this case, the data that this array contains is of the type String.
-
Let’s add the rest of the properties. Each will be made up of a constant assigned to an array that contains strings of data. After the bandTitles property, add the following code:
let bandTitles = ["Nicole Atkins", "Ambulance LTD", "Sleepies", "Black Angels"] let bandSubTitles = ["Tue 5/1", "Fri 5/4", "Sat 5/5", "Sun 5/6"] let bandImageNames = ["thumb-nicole-atkins", "thumb-ambulance-ltd", "thumb-sleepies.png", "thumb-black-angels"] override func viewDidLoad() {
Go to File > Save.
Editing the Three Datasource Protocol Methods
When we created the BandsTableViewController class, Xcode automatically provided us with methods to use. Some of the methods are optional and have been commented out. Others have commented code before them, which are hints that Xcode gives us for methods that we might want to implement to customize the look, feel and data that we want in our app.
- Find the // MARK: - Table view data source comment code.
-
Below that, there are three methods from top to bottom we’ll need to modify to get the band information displaying in our app (don’t worry if these are hard to find, we’ll go through them one by one shortly):
- numberOfSections (a function)
- numberOfRowsInSection (part of the the first tableView function)
- cellForRowAt (part of the the second tableView function and currently commented out)
-
These methods are part of the dataSource protocol. The first method is numberOfSections. This method returns the number of sections so that the BandsTableViewController knows how many to draw and how many to loop through. We’ll just have one section, so change 0 to 1 as shown below:
override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 1 }
-
The next method is numberOfRowsInSection. This method will be called for each section in the table. Since we only have one section, it will only be called once. We need to return the total number of bands that will be drawn. This number will depend on the number of bands we have, which varies, but we can get the number of bands by accessing the size of our array with some simple code. Add the following bold code to the numberOfRowsInSection method, replacing the 0:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return bandTitles.count }
This code takes the array bandTitles and accesses a property called count. The count property gives us the size of the array. After this method has the size of the array, it will tell the View Controller how many times to call the next method, cellForRowAt.
cellForRowAt returns the cell object with all the data it needs to draw the title, subtitle, and image. It is called for each row and it passes in a parameter called indexPath, which will tell us which row is currently being called for. What we need to do next is build a cell object with the data we want the table to draw.
-
We want to use this method, so uncomment it by deleting the symbols shown in bold below:
/* override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) // Configure the cell... return cell } */
-
Find the following line of code:
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
If you remember, earlier in the exercise we set the Cell Identifier of the Nicole Atkins cell in the storyboard to bandCell. To target the cell on our storyboard we need to edit this code.
-
As shown below, change reuseIdentifier to: bandCell
let cell = tableView.dequeueReusableCell(withIdentifier: "bandCell", for: indexPath)
Now that we have the correct cell being targeted, let’s look at the rest of the code. The code in cellForRowAt grabs the cell from the storyboard by using a method dequeueReusableCell to pass in the Identifier cell. The method is called and passes in an indexPath variable to the tableView object. indexPath specifies the section and row that it will be working with. That instantiates a cell object, which we want to further customize by adding our band data into it.
Customizing the Cells
- Below the cellForRowAt method, find the // Configure the cell comment code.
-
This is where we will be adding the code that will customize the cells with our band info. To start with, we need to access the label for the Title in our cell. The Title label is represented by the attribute textLabel. We need to redefine the text in the textLabel in the cell. Add the following bold code:
// Configure the cell... cell.textLabel?.text = return cell
This will target the cell’s textLabel text (the Title) and change it to what we put in the brackets. We want to set it to the info in our array.
-
Add the following bold code:
// Configure the cell... cell.textLabel?.text = bandTitles[indexPath.row] return cell
bandTitles is the name of the array with the Title info. [indexPath.row] gets the info from the array at a specific value. That value is the row that is currently being populated. Each time code passes through a cell configuration it passes the value indexPath to the method. The indexPath is what lets us know what section and what row we are currently dealing with. We access the row number by using indexPath.row. This will, in turn, fetch the corresponding value from the array. You should get used to this process of associating row numbers with array indexes. This is the basis by which most applications will work at the data level.
Let’s see our code in action. Click the Run button. Just to prepare you, the images are going to appear smaller than they did before!
When the app finishes loading, it will open in the Simulator. Notice we have four cells, each with a different band title. You may also notice the cells are less tall than in the previous exercise. That’s because we’re now doing things programmatically. We’ll fix that later in this exercise.
- We still need to set the show date and band image. Switch back to Xcode.
-
To set the value for our detailTextLabel we will follow the same process as we did for our textLabel. Add the following bold code below the textLabel:
// Configure the cell... cell.textLabel?.text = bandTitles[indexPath.row] cell.detailTextLabel?.text = bandSubTitles[indexPath.row] return cell
NOTE: In the code above, we accessed an array via its index. The index was also supplied to us by the table view’s indexPath.
Click the Run button to open the app in the Simulator. The dates should all be different.
- All that’s left is to change the images. Switch back to Xcode.
- Click the Stop button.
-
The code for adding images is a bit more complicated because we only have the names of the images stored, not the actual objects. We’ll need to use a method that will find the file, create an image and assign it to the cell. Let’s start by adding the code that will target the image in the cell like we did for the Title and Subtitle. The attribute for the images in the cell is called imageView. Around line 50, add the following bold code to target the image in the imageView in our cell:
// Configure the cell... cell.textLabel?.text = bandTitles[indexPath.row] cell.detailTextLabel?.text = bandSubTitles[indexPath.row] cell.imageView?.image = return cell
-
To change the image, we’ll use the UIImage class. If you pass in the name of an image to this method, it will find the locally stored image and instantiate an object. Add the following bold code:
// Configure the cell... cell.textLabel?.text = bandTitles[indexPath.row] cell.detailTextLabel?.text = bandSubTitles[indexPath.row] cell.imageView?.image = UIImage(named:) return cell
- Notice the named: inside UIImage(). UIImage expects a string that is the actual filename (name) of the locally stored image. Currently the string is empty, but we will fix that in a second.
-
Now we just need to pass in a string that contains the image name. We can get this from our bandImageNames array similarly to how we got the Title and Subtitle. Add the following bold code:
// Configure the cell... cell.textLabel?.text = bandTitles[indexPath.row] cell.detailTextLabel?.text = bandSubTitles[indexPath.row] cell.imageView?.image = UIImage(named: bandImageNames[indexPath.row]) return cell
Click the Run button.
When the app finishes loading, it will open in the Simulator. Awesome! The content of the cells is now being programmatically added using code.
Specifying Custom Table Cell Height
As we mentioned earlier, the cells are now less tall than what we had in the previous exercise. The custom height of 88 that we used in our static example also needs to come from the code.
There is a method built into the UITableViewDelegate just for this purpose. However, it is not already added for us in the code because it is not required and isn’t always used, so we have to add it ourselves.
- Switch back to Xcode.
- The code we need can be found in the tableView class. Around line 44, find the word UITableView. Hold Control and click on it. From the menu, choose Jump to Definition. This will take you to the actual UITableView Class file.
-
Around line 128, select the line of code shown below:
optional public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
- Hit Cmd–C to copy it.
- In the Project navigator, click BandsTableViewController.swift to return to it.
-
Below the cell configuration code, and after the closing curly brace, make a new line and paste in the code so it appears as follows:
cell.imageView?.image = UIImage(named: bandImageNames[indexPath.row]) return cell } optional public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat /* // Override to support conditional editing of the table view.
-
Now we need to specify the value we want returned. Add the bold code (don’t forget the opening and closing curly braces):
cell.imageView?.image = UIImage(named: bandImageNames[indexPath.row]) return cell } optional public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 88 } /* // Override to support conditional editing of the table view.
-
Then replace optional public with override. It should look like this:
cell.imageView?.image = UIImage(named: bandImageNames[indexPath.row]) return cell } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 88 } /* // Override to support conditional editing of the table view.
-
Click Run.
Now the cells are the right height.
Leave the Xcode project open. We’ll continue to work on it in the next exercise.