Lecture Handout
Last updated
Last updated
A table view (UITableView
) is a subclass of UIScrollView
, which is a type of view that a user can scroll through. Simply put, the main purpose of a table view is to display information in the form of a list. A bit more specifically, a table view consists of sections where each section consists of 3 parts: a header, cells (you can just think of a cell as a row), and a footer.
UITableViews
are an extremely powerful type of view and are used in a ton of successful apps today (i.e. a Spotify playlist, your Gmail inbox, Messenger messages). They provide an easy way to display a lot of information in a compact, organized manner.
Example 1: Clock
Each alarm time is a UITableViewCell
. Each cell has two labels (the time and the "Alarm" description) and a segmented control.
Example 2: Settings
The entire Settings page is a UICollectionView
, with several sections (each "group" of options between the large gray gaps). Each option is a UITableViewCell
.
Besides usual initialization process for initializing any UIView
(calling the init function, set translatesAutoresizingMaskIntoConstraints
to false, adding the view as a subview to parent view, and setting up constraints), there are 3 additional things that you should do to setup a UITableView
:
Set the tableView
’s delegate
Set the tableView
’s dataSource
Register your custom UITableViewCell
class
We will discuss 1 and 2 in the next section. What does 3 mean? Basically, in order to use any custom cell class that we've created inside a tableView
and be able to dequeue it later on, we need to let the tableView
know by registering the cell class and associating it with an identifier (this can be any string).
What does it mean to dequeue a cell and why do we want to do it?
Well, imagine that we were using a table view to display a list of all the students at Cornell. This means that we would have thousands of cells, which would be really expensive in terms of performance and memory! Instead of creating a new cell for each student, as the user scrolls down the list and the cells near the top disappear from the screen, we reuse those cells to display the next set of cells. This way, we will only be creating 5 or 6 cells instead of thousands.
By registering a cell class and associating it with an identifier, all cells with that identifier will try to reuse another cell of that identifier so that the same type of cell can be used to render different content.
The following is an example of initializing a UITableView
:
A UITableViewDelegate
must conform to certain methods and can also provide some optional methods which allows the delegate to manage selections, configure section headings and footers, help to delete and reorder cells, and perform other actions. The one method that you should always implement is one we'll refer to as heightForRowAt
:
This function requires you to return a CGFloat
representing the height (in this example, 50 pixels) that the cell at a certain row or section should have.
Another method that you'll commonly use is one we'll refer to as didSelectRowAt
:
This function is what gets called whenever one of the cells in your table view is selected by the user. Thus, if you want to trigger some sort of action or animation upon selection, this is the place you would do it!
Similarly, a UITableViewDataSource
must conform to certain methods. Unlike the UITableViewDelegate
, however, the role of a UITableViewDataSource
is to provide the table-view object with the information it needs to construct and modify a table view. The first method that you need to implement is one we'll refer to as cellForRowAt
:
This function requires you to return a UITableViewCell
which will be inserted at a particular location in the collectionView
.
One parameter in this function that is important to take note of is indexPath
, which is of type IndexPath
. The indexPath
has two important properties that you may sometimes need:
1. row
and
2. section
Thus, if you wanted to see what row or section the cell you’re returning is going to be used for, you can just call indexPath.row
or indexPath.section
.
Once again, let's go through the code line by line:
2. Dequeue the appropriate cell using the associated reuse identifier that you used when you registered your custom cell to the tableView
. When setting it, make sure you cast the dequeued cell as your custom cell class!
3 & 4. It's best practice to have some configure(...)
function as a part of your custom cell class that takes in some data or model object to configure the views inside your cell. Let's say you're using some dataModelArray
as the data source for your table view. You can access the dataModel object that this row represents using dataModelArray[indexPath.row]
.
5. Return the configured cell!
The other method you must implement is called numberOfItemsInSection
:
This function requires you to return an integer representing the number of rows that the section section
should have. Usually, the number of cells that you want to display in your table view is dependent on the number of model objects that you have. For example, if you wanted to use a table view to display a list of restaurants, you would probably have an array of Restaurant
objects. Thus, the number of cells you need in your table view is just the number of restaurants, which is equivalent to restaurantsArray.count
. However, it is also possible that you just want to create a static number of cells, in which case you can just return that constant!
A common method that's not required, but you may want to implement is numberOfSections
:
As the name implies, this allows you to control the number of sections that your table view has. If you do no implement this method, your table view will by default have 1 section.
Now that we’ve covered the basics of setting up and creating a UITableView
, let's take a look at a real-life example of table views in action and see how we would build it to make sure we fully understand the concepts. The example application we’re going to be looking at is Messenger, used by over 1.3 billion people around the world!
Breaking down this table view, we see that each cell (each conversation) has a couple pieces of information:
A profile image
A user’s name/A group name
The latest message sent/received
The timestamp of that latest message.
Let's assume that all of this information is embodied in some User
model that we've created. Thus, we would probably have an array of User
s in a usersArray
. It also seems that this table view is just comprised of one section (since there are no major breaks between groups of cells).
We'll also assume that the height of each cell is 75 pixels and that upon selecting a cell, we want to push another view controller which displays all the conversation history that the logged in user has had with the user/group of the cell that was clicked on. Putting this all together, we have:
Because we want the number of cells to just be the number of users that we have messaged, in numberOfRowsInSection
, we can just return usersArray.count
.
Here, we arbitrarily want the height of each cell to be 75 pixels but you can choose this to be some other value if you would like.
Here, we dequeue a cell and cast it as a MessengerCell
(which we'll assume is custom cell class that we've already created!).
Next, we get the appropriate User
model for this cell out of the usersArray
, use it to configure the cell, and then finally return the cell.
Note: We have to cast the cell as a MessengerCell
in order to call configure
on cell
, since a generic UITableViewCell
does not have a configure
function.
Here, we have decided to make it so that when a MessengerCell
is tapped, we push a ConversationViewController
(assuming we've already written this view controller class!) after grabbing the User
model associated with this cell.
You can find a complete list of all the UITableViewDelegate
& UITableViewDataSource
methods in the Apple documentation, linked below.