0% found this document useful (0 votes)
24 views

Stanford CS193p: Developing Applications For iOS Spring 2016

This document discusses UITableView and UITableViewController in iOS. It covers the different styles of UITableView (plain and grouped), sections, cell types, static and dynamic cells, customizing cell attributes, and creating custom cell subclasses. The document provides examples and screenshots to illustrate concepts related to using table views and table view controllers in iOS applications.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views

Stanford CS193p: Developing Applications For iOS Spring 2016

This document discusses UITableView and UITableViewController in iOS. It covers the different styles of UITableView (plain and grouped), sections, cell types, static and dynamic cells, customizing cell attributes, and creating custom cell subclasses. The document provides examples and screenshots to illustrate concepts related to using table views and table view controllers in iOS applications.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

Stanford CS193p

Developing Applications for iOS


Spring 2016

CS193p

Spring 2016
Today
Table View
Way to display large data sets
Demo: Twitter Client

CS193p

Spring 2016
UITableView
UITableViewStyle.Plain .Grouped

Dynamic (List)

Static

& Plain
& Grouped
(ungrouped)

CS193p

Spring 2016
UITableView
Plain Style
Table Header

var tableHeaderView: UIView CS193p

Spring 2016
UITableView
Plain Style
Table Header

Table Footer

var tableFooterView: UIView CS193p

Spring 2016
UITableView
Plain Style
Table Header

Section

Table Footer

CS193p

Spring 2016
UITableView
Plain Style
Table Header
Section Header

Section

Table Footer

UITableViewDataSource’s tableView(UITableView, titleForHeaderInSection: Int) CS193p

Spring 2016
UITableView
Plain Style
Table Header
Section Header

Section Footer

Section

Table Footer

UITableViewDataSource’s tableView(UITableView, titleForFooterInSection: Int) CS193p

Spring 2016
UITableView
Plain Style
Table Header
Section Header
Table Cell

Section Footer

Section

Table Footer

UITableViewDataSource’s tableView(UITableView, cellForRowAtIndexPath: NSIndexPath) CS193p

Spring 2016
UITableView
Plain Style
Table Header
Section Header
Table Cell

Section Footer

Section

Table Footer

CS193p

Spring 2016
UITableView
Grouped Style
Table Header

Section Header
Table Cell

Section Footer

Section

Table Footer

CS193p

Spring 2016
Sections or Not

No Sections Sections
CS193p

Spring 2016
Cell Type

Subtitle Basic Right Detail Left Detail


UITableViewCellStyle.Subtitle .Default .Value1 .Value2 CS193p

Spring 2016
The class UITableViewController
provides a convenient packaging of a
UITableView in an MVC.

It’s mostly useful when

the UITableView is going to fill all of self.view

(in fact self.view in a UITableViewController is the UITableView).

You can add one to your


storyboard simply by
dragging it from here.
CS193p

Spring 2016
Controller: (subclass of) UITableViewController

Controller’s view property: the UITableView

CS193p

Spring 2016
Like any other View Controller,

you’ll want to set its class in the


Identity Inspector.

CS193p

Spring 2016
Just use

File -> New File …


as usual.
Make sure you set the
superclass to
UITableViewController

CS193p

Spring 2016
… otherwise it won’t
make sense to set it as
the class here.

CS193p

Spring 2016
Your UITableViewController subclass
will also serve as the

UITableView’s dataSource and delegate

(more on this in a moment).

You can see that if you right-click


the Controller here.

dataSource and delegate


properties

If you use UITableView without UITableViewController, you’ll have to wire these up yourself.

CS193p

Spring 2016
You can edit
attributes of the
UITableView by
inspecting it.

Remember that you can shift-right-click

(or ctrl-shift-left-click)

on things to pick exactly what you want

from what is under the mouse.

This makes it easier to pick the Controller, the


table view, a cell in a table view, or even a
view inside a cell in the table view.

CS193p

Spring 2016
One important attribute is the
Plain vs. Grouped style …

CS193p

Spring 2016
CS193p

Spring 2016
Grouped

CS193p

Spring 2016
Grouped

Another important attribute is


Dynamic versus Static …

CS193p

Spring 2016
Grouped

Static means that these cells are set up


in the storyboard only.

You can edit them however you want


including dragging buttons, etc., into them

(and wiring up outlets to the Controller).

CS193p

Spring 2016
A more interesting table,
however, is a Dynamic one …
Grouped

CS193p

Spring 2016
Grouped

… which we almost
always use in Plain style.

CS193p

Spring 2016
These cells are now templates which will be
repeated for however many rows are needed to
display the data in MVC’s Model.

CS193p

Spring 2016
Any cell can be inspected

in the Attributes Inspector …

… to set things like

the style of the cell.

CS193p

Spring 2016
Subtitle cell style

CS193p

Spring 2016
You can also set a
symbol to appear on the
right of the cell.

This one’s sort of special …

CS193p

Spring 2016
We’ll talk about this Detail Disclosure button in a bit.

CS193p

Spring 2016
CS193p

Spring 2016
CS193p

Spring 2016
One of the
cell styles you
can choose is
Custom.

CS193p

Spring 2016
Like the cells in a static table view,

Custom style cells can have UI built inside them.

CS193p

Spring 2016
You can change their size.

CS193p

Spring 2016
And you can drag UI
elements into them.

CS193p

Spring 2016
It is important to set proper autolayout
constraints if you want your Custom cells to adjust
their height automatically to their content.

CS193p

Spring 2016
CS193p

Spring 2016
CS193p

Spring 2016
To wire up any outlets, though, you have to
create a custom subclass of the class of UIView
that is in these cells: UITableViewCell.

You do this with the


Identity Inspector.

CS193p

Spring 2016
You create a custom subclass of
UITableViewCell just like any other
subclass. Using File -> New File …

CS193p

Spring 2016
CS193p

Spring 2016
Choose UITableViewCell as
the class to subclass off of.

CS193p

Spring 2016
Then set it in the Identity
Inspector as usual.

CS193p

Spring 2016
Now you can wire up UI outlets and
actions to the UI elements in the cell.

CS193p

Spring 2016
Just open up your

new UITableViewCell subclass

in the Assistant Editor

(Automatic does not seem to work).

And then ctrl-drag as usual.

CS193p

Spring 2016
Remember that this is a “prototype” cell, so
there will be an instance of this cell for every
visible row (each with its own UI and outlets).

CS193p

Spring 2016
UITableView Protocols
How to connect all this stuff up in code?
Connections to code are made using the UITableView‘s dataSource and delegate
The delegate is used to control how the table is displayed (it’s look and feel)
The dataSource provides the data that is displayed inside the cells

UITableViewController automatically sets itself as the UITableView’s delegate & dataSource


Your UITableViewController subclass will also have a property pointing to the UITableView …
var tableView: UITableView // self.view in UITableViewController

When do we need to implement the dataSource?


Whenever the data in the table is dynamic (i.e. not static cells)
There are three important methods in this protocol …
How many sections in the table?
How many rows in each section?
Give me a view to use to draw each cell at a given row in a given section.
Let’s cover the last one first (since the first two are very straightforward) ...
CS193p

Spring 2016
Customizing Each Row
Providing a UIView to draw each row …
It has to be a UITableViewCell (which is a subclass of UIView) or subclass thereof
Don’t worry, if you have 10,000 rows, only the visible ones will have a UITableViewCell
But this means that UITableViewCells are reused as rows appear and disappear
This has ramifications for multithreaded situations, so be careful in that scenario

The UITableView will ask its UITableViewDataSource for the UITableViewCell for a row …
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{

NSIndexPath is just a container to pass you


} the section and row in question.

CS193p

Spring 2016
Customizing Each Row
Providing a UIView to draw each row …
It has to be a UITableViewCell (which is a subclass of UIView) or subclass thereof
Don’t worry, if you have 10,000 rows, only the visible ones will have a UITableViewCell
But this means that UITableViewCells are reused as rows appear and disappear
This has ramifications for multithreaded situations, so be careful in that scenario

The UITableView will ask its UITableViewDataSource for the UITableViewCell for a row …
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let data = myInternalDataStructure[indexPath.section][indexPath.row]

CS193p

Spring 2016
Customizing Each Row
Providing a UIView to draw each row …
It has to be a UITableViewCell (which is a subclass of UIView) or subclass thereof
Don’t worry, if you have 10,000 rows, only the visible ones will have a UITableViewCell
But this means that UITableViewCells are reused as rows appear and disappear
This has ramifications for multithreaded situations, so be careful in that scenario

The UITableView will ask its UITableViewDataSource for the UITableViewCell for a row …
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let data = myInternalDataStructure[indexPath.section][indexPath.row]

let cell = . . . // create a UITableViewCell and load it up with data

return cell
}

CS193p

Spring 2016
Customizing Each Row
Providing a UIView to draw each row …
It has to be a UITableViewCell (which is a subclass of UIView) or subclass thereof
Don’t worry, if you have 10,000 rows, only the visible ones will have a UITableViewCell
But this means that UITableViewCells are reused as rows appear and disappear
This has ramifications for multithreaded situations, so be careful in that scenario

The UITableView will ask its UITableViewDataSource for the UITableViewCell for a row …
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let data = myInternalDataStructure[indexPath.section][indexPath.row]

let cell = . . . // create a UITableViewCell and load it up with data

return cell
}
}

CS193p

Spring 2016
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let data = myInternalDataStructure[indexPath.section][indexPath.row]

CS193p

Spring 2016
func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let data = myInternalDataStructure[indexPath.section][indexPath.row]

}
CS193p

Spring 2016
This method gets a UITableViewCell for
us either by reusing one that has gone off
screen or by making a copy of one of our
prototypes in the storyboard.

This String tells iOS which


prototype to copy or reuse.

func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell


{
let data = myInternalDataStructure[indexPath.section][indexPath.row]
let dequeued = tv.dequeueReusableCellWithIdentifier(“MyCell”, forIndexPath: indexPath)

}
CS193p

Spring 2016
For a non-Custom cell …

… the dequeued thing will be a generic UITableViewCell.

You can look up its API to see what sort of configuration


options are available for it.

func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell


{
let data = myInternalDataStructure[indexPath.section][indexPath.row]
let dequeued = tv.dequeueReusableCellWithIdentifier(“MyCell”, forIndexPath: indexPath)

dequeued.textLabel?.text = data.importantInfo
dequeued.detailTextLabel?.text = data.lessImportantInfo
return cell
}
CS193p

Spring 2016
For a Custom cell …

func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell


{
let data = myInternalDataStructure[indexPath.section][indexPath.row]
let dequeued = tv.dequeueReusableCellWithIdentifier(“MyCustomCell”, forIndexPath: indexPath)

return cell
}
CS193p

Spring 2016
… the dequeued thing will be your subclass of UITableViewCell.

You will use its public API to configure it

(i.e. that public API will set the values of its outlets, etc.).

func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell


{
let data = myInternalDataStructure[indexPath.section][indexPath.row]
let dequeued = tv.dequeueReusableCellWithIdentifier(“MyCustomCell”, forIndexPath: indexPath)
if let cell = dequeued as? MyTableViewCell {
cell.publicAPIofMyTableViewCell = data.theDataTheCellNeedsToDisplayItsCustomLabelsEtc
}
return cell
}
CS193p

Spring 2016
UITableViewDataSource
How does a dynamic table know how many rows there are?
And how many sections, too, of course?
Via these UITableViewDataSource protocol methods …
func numberOfSectionsInTableView(sender: UITableView) -> Int
func tableView(sender: UITableView, numberOfRowsInSection: Int) -> Int

Number of sections is 1 by default


In other words, if you don’t implement numberOfSectionsInTableView, it will be 1

No default for numberOfRowsInSection


This is a required method in this protocol (as is cellForRowAtIndexPath)

What about a static table?


Do not implement these dataSource methods for a static table
UITableViewController will take care of that for you
You edit the data directly in the storyboard

CS193p

Spring 2016
UITableViewDataSource
Summary
Loading your table view with data is simple …
1. set the table view’s dataSource to your Controller (automatic with UITableViewController)
2. implement numberOfSectionsInTableView and numberOfRowsInSection
3. implement cellForRowAtIndexPath to return loaded-up UITableViewCells

Section titles are also considered part of the table’s “data”


So you return this information via UITableViewDataSource methods …
func tableView(UITableView, titleFor{Header,Footer}InSection: Int) -> String
If a String is not sufficient, the UITableView’s delegate can provide a UIView

There are a number of other methods in this protocol


But we’re not going to cover them in lecture
They are mostly about dealing with editing the table by deleting/moving/inserting rows
That’s because when rows are deleted, inserted or moved, it would likely modify the Model
(and we’re talking about the UITableViewDataSource protocol here)

CS193p

Spring 2016
How do you segue when a row is touched?
Just ctrl-drag from a prototype (or static) row to another MVC of course …

CS193p

Spring 2016
You can choose from any of
the segues you are used to.

CS193p

Spring 2016
If you have a Detail Disclosure Accessory,

you can hook up a segue for that too.

CS193p

Spring 2016
Just set the identifier as usual.

Let’s take a look at

prepareForSegue

for this segue…

CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case “XyzSegue”: // handle XyzSegue here
case “AbcSegue”:

default: break
}
}
}

You can see now why sender is AnyObject

Sometimes it’s a UIButton, sometimes it’s a UITableViewCell


CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case “XyzSegue”: // handle XyzSegue here
case “AbcSegue”:
if let cell = sender as? MyTableViewCell {

}
default: break
}
}
}

So you will need to cast sender with as? to turn it into a UITableViewCell
If you have a custom UITableViewCell subclass, you can cast it to that if it matters
CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
indexPathForCell does not
switch identifier { accept AnyObject.

case “XyzSegue”: // handle XyzSegue here It has to be a UITableViewCell

case “AbcSegue”: of some sort.


if let cell = sender as? MyTableViewCell,
let indexPath = tableView.indexPathForCell(cell) {

}
default: break
}
}
}

Usually we will need the NSIndexPath of the UITableViewCell

Because we use that to index into our internal data structures


CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case “XyzSegue”: // handle XyzSegue here
case “AbcSegue”:
if let cell = sender as? MyTableViewCell,
let indexPath = tableView.indexPathForCell(cell),
let seguedToMVC = segue.destinationViewController as? MyVC {

}
default: break
}
}
}

Now we just get our destination MVC as the proper class as usual …

CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case “XyzSegue”: // handle XyzSegue here
case “AbcSegue”:
if let cell = sender as? MyTableViewCell,
let indexPath = tableView.indexPathForCell(cell),
let seguedToMVC = segue.destinationViewController as? MyVC {
seguedToMVC.publicAPI = data[indexPath.section][indexPath.row]
}
default: break
}
}
}

and then get data from our internal data structure using the NSIndexPath’s section and row

CS193p

Spring 2016
Table View Segues
Preparing to segue from a row in a table view

The sender argument to prepareForSegue is the UITableViewCell of that row …


func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case “XyzSegue”: // handle XyzSegue here
case “AbcSegue”:
if let cell = sender as? MyTableViewCell,
let indexPath = tableView.indexPathForCell(cell),
let seguedToMVC = segue.destinationViewController as? MyVC {
seguedToMVC.publicAPI = data[indexPath.section][indexPath.row]
}
default: break
}
}
}

and then get data from our internal data structure using the NSIndexPath’s section and row

and use that information to prepare the segued-to API using its public API
CS193p

Spring 2016
UITableViewDelegate
So far we’ve only talked about the UITableView’s dataSource
But UITableView has another protocol-driven delegate called its delegate

The delegate controls how the UITableView is displayed


Not the data it displays (that’s the dataSource’s job), how it is displayed

Common for dataSource and delegate to be the same object


Usually the Controller of the MVC containing the UITableView
Again, this is set up automatically for you if you use UITableViewController

The delegate also lets you observe what the table view is doing
Especially responding to when the user selects a row
Usually you will just segue when this happens, but if you want to track it directly …

CS193p

Spring 2016
UITableView “Target/Action”
UITableViewDelegate method sent when row is selected
This is sort of like “table view target/action” (only needed if you’re not segueing, of course)
Example: if the master in a split view wants to update the detail without segueing to a new one
func tableView(sender: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// go do something based on information about my Model
// corresponding to indexPath.row in indexPath.section
// maybe directly update the Detail if I’m the Master in a split view?
}

Delegate method sent when Detail Disclosure button is touched


func tableView(UITableView, accessoryButtonTappedForRowWithIndexPath: NSIndexPath)
Again, you can just segue from that Detail Disclosure button if you prefer

CS193p

Spring 2016
UITableViewDelegate
Lots and lots of other delegate methods
will/did methods for both selecting and deselecting rows
Providing UIView objects to draw section headers and footers
Handling editing rows (moving them around with touch gestures)
willBegin/didEnd notifications for editing
Copying/pasting rows

CS193p

Spring 2016
UITableView
What if your Model changes?
func reloadData()
Causes the UITableView to call numberOfSectionsInTableView and numberOfRowsInSection
all over again and then cellForRowAtIndexPath on each visible row
Relatively heavyweight, but if your entire data structure changes, that’s what you need
If only part of your Model changes, there are lighter-weight reloaders, for example ...
func reloadRowsAtIndexPaths(indexPaths: [NSIndexPath],
withRowAnimation: UITableViewRowAnimation)

CS193p

Spring 2016
UITableView
Controlling the height of rows
Row height can be fixed (UITableView’s var rowHeight: CGFloat)
Or it can be determined using autolayout (rowHeight = UITableViewAutomaticDimension)
If you do automatic, help the table view out by setting estimatedRowHeight to something
The UITableView’s delegate can also control row heights …
func tableView(UITableView, {estimated}heightForRowAtIndexPath: NSIndexPath) -> CGFloat
Beware: the non-estimated version of this could get called A LOT if you have a big table

CS193p

Spring 2016
UITableView
There are dozens of other methods in UITableView itself
Setting headers and footers for the entire table.
Controlling the look (separator style and color, default row height, etc.).
Getting cell information (cell for index path, index path for cell, visible cells, etc.).
Scrolling to a row.
Selection management (allows multiple selection, getting the selected row, etc.).
Moving, inserting and deleting rows, etc.
As always, part of learning the material in this course is studying the documentation

CS193p

Spring 2016

You might also like