Drag the table view up and down with your finger, and click on a cell.
The cells in the first screenshot are of style
UITableViewCellStyle.default
,
which have only a
textLabel
.
The cells in the second screenshot are of style
UITableViewCellStyle.subtitle
,
which also have a
detailTextLabel
.
The Alaska cell has an
imageView
containing a
non-nil
UIImage
,
and the California cell has been selected by the user.
AppDelegate
:
unchanged.
TableViewController
also acts as the table view’s
data
source
and
delegate.
Main.storyboard
Assets.xcassets
,
an Xcode
asset catalog
file.
AppIcon.iconset
Contents.json
:
a
JSON
file listing files belonging to the icon set.Alaska.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Alaska image set.Alaska.jpg
:
one of the files belonging to the Alaska image set.
Apple prefers
.png
format.
48 × 32 pixels.
Info.plist
:
unchanged.
Select the
States
folder in the Xcode
Project
Navigator.
File →
New →
File…
Chose a template for your new file:
iOS
Source: Cocoa Touch Class
Next
Choose options for your new file:
Class:
TableViewController
Subclass of:
UITableViewController
Next
Create
Select
Main.storyboard
in the Xcode
Project
Navigator.
Open the left pane of the center panel of Xcode as far as
▼ View Controller Scene
▶ View Controller
First Responder
Exit
and select the View Controller.
In the right panel of Xcode,
click on the icon for the Identity inspector.
It’s a rectangle with a smaller rectangle in its upper left corner.
Open the left pane of the center panel of Xcode as far as
▼ View Controller Scene
▼ View Controller
▶ View
In the Identity Inspector,
change the class of the view from
UIView
to
UITableView
.
Control-click on
ViewController.swift
in the Xcode
Project Navigator
and select Delete.
Do you want to move the file “ViewController.swift”
to the Trash, or only remove the reference to it?
Move to Trash.
Select
Assets.xcassets
in the Xcode
Project Navigator.
Drag
Alaska.jpg
into the left pane of the center panel of Xcode, under AppIcon.
The Settings app has lots of examples of table views.
UITableView
,
UITableViewController
,
and
UITableViewCell
UITableViewDataSource
and
UITableViewDelegate
A
table view
is composed of sections,
which are composed of cells called
UITableViewCell
s.
A
table view
can scroll because it is derived from class
UIScrollView
.
In the
viewDidLoad()
method of the
TableViewController
,
we configure some of the properties that the table view inherits from
UIScrollView
.
Every table view must have a
data
source
object which must conform to protocol
UITableViewDataSource
.
In order to conform to this protocol,
it must have the following three methods.
numberOfSections(in:)
returns 1 in this app.
tableView(_:numberOfRowsInSection:)
returns 50 in this app.
tableView(_:cellForRowAtIndexPath:)
returns a
UITableViewCell
,
preferably recycled.
See the
cellReuseIdentifier
instance variable of the
TableViewController
.
Think of the data source as the “model” object,
and the table view as the “view” object.
(And, obviously,
the view controller is the “controller” object.)
The
UIPickerView
we saw
here
has a
UIPickerViewDataSource
.
If we want the cells of a table view to do something
when we touch them
(other than to turn gray),
we must provide the table view with a
delegate
object.
This object must conform to protocol
UITableViewDelegate
.
When we touch a cell, the
tableView(_:didSelectRowAtIndexPath:)
method of the delegate is automatically called.
The
UIPickerView
we saw
here
has a
UIPickerViewDelegate
.
A view controller immediately above a table view must be a special type of
view controller called a
UITableViewController
.
An ordinary
UIViewController
contains a property named
view
that refers to its
UIView
.
An
UITableViewController
contains a property named
tableView
that refers to its
UITableView
.
A
UITableViewController
can also act as the table view’s data source and delegate,
because the
UITableViewController
conforms to protocols
UITableViewDataSource
and
UITableViewDelegate
.
Our
TableViewController
plays all three of these rôles.
The most important method we give to a view controller is usually
loadView()
,
which creates the view controller’s view.
But we don’t have to write any
loadView()
for a
UITableViewController
.
The
UITableViewController
is hardwired to always create a
UITableView
.
Similarly, we do not have to set the
dataSource
and
delegate
properties of a
table
view
created by a
table
view controller.
These properties already point to the
table
view controller.
UILabel
properties
called the
textLabel
(the title)
and the
detailTextLabel
(the subtitle).
viewDidLoad()
method of the
TableViewController
,
change some of the properties of the table view.
textLabel
.
Insert the following code immediately before the
return
statement in the
tableView(_:cellForRowAtIndexPath:)
method of the
TableViewController
.
Run the app and scroll down to Wyoming.
On iPhone 8 Plus in any orientation,
each cell is 45 points high.
44⅔ of them are occupied by the
textlabel
,
and the one remaining pixel (⅓ of a point) is a
hairline along the bottom edge of the cell.
These defaults are not in the documentation,
and are not guaranteed by Apple.
I measured them
so you could anticipate how many lines will fit on the screen,
and make the rest of your app consistent with the table view.
if cell.textLabel!.text! == "Wyoming" { print("cell.bounds.size = \(cell.bounds.size)"); print("cell.textLabel!.frame = \(cell.textLabel!.frame)"); print("cell.textLabel!.font.fontName = \(cell.textLabel!.font.fontName)"); print("cell.textLabel!.font.pointSize = \(cell.textLabel!.font.pointSize)"); }
cell.bounds.size = (414.0, 45.0) cell.textLabel!.frame = (20.0, 0.0, 374.0, 44.6666666666667) cell.textLabel!.font.fontName = .SFUIText cell.textLabel!.font.pointSize = 17.0
dequeueReusableCell(withIdentifier:for:)
method of a
UITableView
creates cells of style
UITableViewCellStyle.default
.
Let’s change the style to
UITableViewCellStyle.subtitle
.
Add the following class to the project.
It will be exactly the same as class
UITableViewCell
,
except that the style is
UITableViewCellStyle.subtitle
instead of
UITableViewCellStyle.default
.
Select the
States
folder in the Xcode
Project Navigator.
File →
New →
File…
Chose a template for your new file:
iOS Source/Cocoa Touch Class
Next
Choose options for your new file:
Class:
TableViewCell
Subclass of:
UITableViewCell
Next
Create
TableViewCell
.
//called by dequeueReusableCell(withIdentifier:for:) override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: UITableViewCellStyle.subtitle, reuseIdentifier: reuseIdentifier); } //never called required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)!; }In the
viewDidLoad()
method of the
TableViewController
,
the following statement specifies the class of the cell returned by each call to
dequeueReusableCell(withIdentifier:for:)
.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier);Change it to the following.
tableView.register(TableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier);Insert the following statement after setting the
cell.textLabel!.text
in the
tableView(_:cellForRowAtIndexPath:)
method of the
TableViewController
.
cell.detailTextLabel!.text = "\(states[indexPath.row]) is state \(indexPath.row + 1) out of \(states.count).";After running the app, try the other styles:
UITableViewCellStyle.value1
and
UITableViewCellStyle.value2
.
For
value2
,
you’ll have to comment out the
cell.imageView
property.
tableView(_:didSelectRowAt:)
method,
called automatically when a cell is touched.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let cell: UITableViewCell? = tableView.cellForRow(at: indexPath); if cell != nil { cell!.accessoryType = UITableViewCellAccessoryType.checkmark; } }Better yet, toggle the checkmark instead of always setting it. Change the last statement to the following.
if cell!.accessoryType == UITableViewCellAccessoryType.none { cell!.accessoryType = UITableViewCellAccessoryType.checkmark; } else { cell!.accessoryType = UITableViewCellAccessoryType.none; }A more cryptic way to say the above is
cell!.accessoryType = cell!.accessoryType == UITableViewCellAccessoryType.none ? UITableViewCellAccessoryType.checkmark : UITableViewCellAccessoryType.none;
states
array from a text file when the app is launched.
Select the Supporting Files folder in the Xcode
Project Navigator.
states.txt
in the
Project
Navigator.
Change its contents to the following,
one state per line.
Alabama Alaska Arizona Arkansas California Colorado Connecticut Delaware Florida Georgia Hawaii Idaho Illinois Indiana Iowa Kansas Kentucky Louisiana Maine Maryland Massachusetts Michigan Minnesota Mississippi Missouri Montana Nebraska Nevada New Hampshire New Jersey New Mexico New York North Carolina North Dakota Ohio Oklahoma Oregon Pennsylvania Rhode Island South Carolina South Dakota Tennessee Texas Utah Vermont Virginia Washington West Virginia Wisconsin WyomingChange the
states
property of the
TableViewController
to the following.
var states: [String] = [String](); //an empty array of Strings
Add the following method to the
TableViewController
.
required init?(coder aDecoder: NSCoder) { let bundle: Bundle = Bundle.main; let filename: String? = bundle.path(forResource: "states", ofType: "txt"); if filename == nil { print("couldn't find file states.txt"); } else { do { let s: String? = try String(contentsOfFile: filename!, encoding: String.Encoding.utf8); states = s!.components(separatedBy: "\n"); } catch { print("couldn't read file states.txt: \(error)"); } } super.init(coder: aDecoder); }