This article is part of a collection of articles about Swift Basics, where I try to explain different parts of Swift development in a more understandable way.
In most iOS applications, you will probably need to use a UITableView
at some point. In this short guide, you will find out how to create a UITableView
, and populate it with your own data.
UITableView
As Apple say in the documentation, “A table view displays a list of items in a single column.”. Basically, it’s a list of cells, that you can take complete control over. With a UITableView
object, there are two ways in which you can control them. They are the UITableViewDelegate, and the UITableViewDataSource
. The delegate is what manages the interactions on the table cells, such as selecting and reordering. The UITableViewDataSource
is as you may of guessed, what controls the data that populates the table, and also configures the cells in the Table View. However in this guide, we will just be focussing on the DataSource
, as this is all we need to populate a TableView with data.
Styles
There are various ways in which you can customise the style of the TableView manually, but there are two main styles that you can choose from in the InterfaceBuilder, “Grouped” or “Plain”. There aren’t many differences, but in the “Plain” style, each cell fills the Table, and the relevant header/footer float above the cells. However in the “Grouped” style, the sections are visually separated into groups, with the addition of a background colour.
Creating the Project
We’ll start with a new Xcode project. So create a new iOS project, and select the “Single View Application” template. Then you can give it a name, for this example I’m using “TableViewExample”.
Just make sure the Language is Swift, and the other options aren’t needed for this example. This will create all the necessary files, such as “AppDelegate.swift”, “ViewController.swift”, and “Main.storyboard”.
Putting a UITableView on the Screen
Click on the “Main.storyboard” file, and from the object library on the right, drag a Table View onto the view (There should only be the one).
Then to make sure it works on all iOS devices and screen sizes, we’ll set up the layout. Just drag the corners of the TableView so that it fills the view, but keeps the top status bar visible, it should automatically align. Select the Table View, and then open up the “Add New Constraints” view from the bottom right corner. It is the icon with a square, which has a vertical line either side.
Once you constraint view appears, select the four lines around the square at the top, while making sure each value is 0. This will simply make it fit to the edges on any screen size.
Configuring the UITableView
Now we need to configure the Table View so that we can manage it later on. So select the Table View, and then click to show the “Attributes Inspector” from the right sidebar.
The only values you need to change are:
- Content – Dynamic Prototypes
- Prototype Cells – 1
- Style – Grouped
All we are doing with these settings, is making the cells in the table dynamic, so that we can update them with live data later on. Then we set the style to grouped, so we can see each section better visually.
UITableViewCell
As we also want to populate the cells with some data later on, we will make use of the dynamic prototyping in Interface Builder, to design the cells.
Firstly we will set a “Reuse Identifier” for the cell, this is so that we can reuse the same cell prototype when loading the TableView. To do this, just select the cell (it may be easier to do this in the Document Outline to the left”, and while still showing the “Attributes Inspector”, set the Reuse Identifier to “PlainCell”.
Linking the UITableView to the ViewController
We will manage the Table View from the initial View Controller, so the next step is to set it as the DataSource of the Table. You can do this programatically, but for this example we will do it using Interface Builder. To do this, select the Table View, and then choose the “Connections Inspector” in the right sidebar.
From there, just click and drag from the open circle to the right of “delegate” and “dataSource” to the View Controller icon at the top of the view. Then in the “ViewController.swift” file, we will need to set the class to be the DataSource. Just replace the current class definition:
class ViewController: UIViewController {
To the following:
class ViewController: UIViewController, UITableViewDataSource {
It will show some errors right now, but that is just because we haven’t implemented the required functions yet.
Populating the Table View
We have the Table View set up in Interface Builder, and it’s linked to the View Controller, so now it’s time to populate it with real data. At the top of the View Controller class, just below the code you’ve just written, copy the below code:
let sections = ["Fruit", "Vegetables"]
let fruit = ["Apple", "Orange", "Mango"]
let vegetables = ["Carrot", "Broccoli", "Cucumber"]
This is just three arrays that will be used for the section headings, and the content of each of them.
UITableViewDataSource
To fill the table with data, we need to write four functions to do the following:
- Set the headings for each section.
- Set the total number of sections.
- Set the total number of rows in each section.
- Configure the individual cells with the relevant data.
Section Headings
We already defined the section headings before, so all we need to do here is to return the string for the associated section, which the function receives as an Int.
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
Number of Sections
This is quite a simple method, and it just tells the TableView how many sections there are. We already have the section headings in an Array, so we can just return the count
value of this.
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
Number of Rows
Very similar to the method above, but this time we have slightly more complexity in how we return the value of the number of rows. As this method is for every section, we first need to check which section it is for, and then return the count
value of the relevant array.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
// Fruit Section
return fruit.count
case 1:
// Vegetable Section
return vegetables.count
default:
return 0
}
}
Configure the Cell
The last part of populating the Table View, is to load the data into the cell. We do this by first creating a UITableViewCell
object, by making use of the dequeueReusableCell(withIdentifier:)
function. This uses the Reuse Identifier we set earlier, to dynamically reuse one of the cells that have already been created. Then depending on the section that the cell is in, we set the text of the text label of the cell, to the value from the relevant array. After this, the cell is returned, and it is displayed.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create an object of the dynamic cell "PlainCell"
let cell = tableView.dequeueReusableCell(withIdentifier: "PlainCell", for: indexPath)
// Depending on the section, fill the textLabel with the relevant text
switch indexPath.section {
case 0:
// Fruit Section
cell.textLabel?.text = fruit[indexPath.row]
break
case 1:
// Vegetable Section
cell.textLabel?.text = vegetables[indexPath.row]
break
default:
break
}
// Return the configured cell
return cell
}
Results
If you run this project now, it should look like this:
Final Code
Here is the final code for the View Controller that we created:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let sections = ["Fruit", "Vegetables"]
let fruit = ["Apple", "Orange", "Mango"]
let vegetables = ["Carrot", "Broccoli", "Cucumber"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
// MARK: UITableViewDataSource
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
// Fruit Section
return fruit.count
case 1:
// Vegetable Section
return vegetables.count
default:
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create an object of the dynamic cell "PlainCell"
let cell = tableView.dequeueReusableCell(withIdentifier: "PlainCell", for: indexPath)
// Depending on the section, fill the textLabel with the relevant text
switch indexPath.section {
case 0:
// Fruit Section
cell.textLabel?.text = fruit[indexPath.row]
break
case 1:
// Vegetable Section
cell.textLabel?.text = vegetables[indexPath.row]
break
default:
break
}
// Return the configured cell
return cell
}
}
You can find the example project on GitHub.
Congratulations! You’ve now developed an app that makes use of a UITableView, along with a UITableViewDataSource to dynamically populate data, and also to configure the UITableViewCell. As this is an ongoing series of articles, you can expect more guides on how to take this project even further.