Click anywhere on a big yellow view (except where it’s covered by the translucent navigation bar) to go east to the next station of the L subway.
The status bar is the top 20 pairs of pixels of the iPhone 6 screen. It displays the current time, battery level, etc. Under the status bar is the navigation bar. See Bars.
In portrait orientation,
the heights of the status and navigation bars are 20 and 44 pairs of pixels,
for a total of 64.
We therefore set the yellow view’s
bounds.origin
to (–64, 0),
which puts the origin (0, 0) at the lower left corner of the navigation bar.
In landscape orientation,
the height of the navigation bar is 32 pairs of pixels.
bounds.origin
is therefore set to (–32, 0),
which puts the origin (0, 0) at the lower left corner of the navigation bar.
The status and navigation bars, not to mention the back button, looked better in iOS 6. What were they thinking of?
The tab bar controller we saw here let us visit the subordinate view controllers in any order. A navigation controller lets us visit the subordinate view controllers in only one specific order: Eighth Avenue, Sixth Avenue, Union Square, etc., and back again. You can’t jump directly from Eighth Avenue to Union Square without visiting Sixth Avenue along the way.
The classic example of a
navigation
controller
is in the
Settings
app that comes with the iPhone.
The navigation controller provides the
right-to-left animation as we drill deeper into the menus,
and left-to-right animation as we come back out.
It does not provide the menus themselves—they will come later,
when we have class
UITableView
.
Even if we have only one view controller and one view, we might still want to put a navigation controller atop the view controller just to make the view controller’s navigation bar visible. Every view controller has a navigation bar, but the navigation bar is visible only if there is a navigation controller above the view controller. The navigation bar can contain attractive titles and buttons.
The
Main.storyboard
file creates the
LineController
object,
whose class is a subclass of class
UINavigationController
.
The
LineController
contains an
array
of one or more
StationController
s,
one for each station on the L Train,
Manhattan’s only east/west subway line.
Each
StationController
s,
creates one
StationView
.
The
init
method of class
LineController
creates only the first
StationController
,
not all of them,
because it might be expensive to create objects of this class.
We create the
StationController
s
only as needed.
AppDelegate
:
unchanged.
LineController
is a subclass of class
UINavigationController
,
which is a subclass of class
UIViewController
.
This app has only one object of class
LineController
.
StationController
is a subclass of class
UIViewController
.
Underneath the
LineController
,
this app has an array containing (eventually up to) six objects of class
StationController
.
StationView
.
Each
StationController
creates one object of this class.
We had to set the view’s
contentMode
in
Tabbar.
The
\n
in
drawRect(_:)
is the
newline
character.
Class
StationView
is a subclass of class
UIView
,
class
StationController
is a subclass of class
UIViewController
,
and class
LineController
is a subclass of class
UINavigationController
.
After creating these three classes,
go to the Xcode Project Navigator
and select the file
Main.storyboard
.
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.
In the Xcode Project Navigator,
control-click on the file
ViewController.swift
and select Delete.
Do you want to move the file "ViewController.swift" to the Trash?
Move to Trash.
init
method of class
StationView
,
change the
background
color
to the following.
We saw the function
arc4random
in
Puzzle.
With no parameter,
it returns a random
UInt32
in the range 0 to
UInt32.max
= 4,294,967,295
inclusive.
Each quotient is therefore in the range 0.0 to
Without the conversions to a floating point type,
the quotients would be 0.
1.0 inclusive.
backgroundColor = UIColor( red: CGFloat(arc4random()) / CGFloat(UInt32.max), green: CGFloat(arc4random()) / CGFloat(UInt32.max), blue: 1.0, alpha: 1.0);
next
method of class
LineController
,
what happens if you change the
animated:
to
false
?
After you find out,
change it back to
true
.
title
of the view controller that is currently displayed by the navigation controller.
To add a prompt to the top of the navigation bar
(occcupying an additional 30 pixels),
insert the following statement into the
init
method of class
StationController
after setting the
title
.
navigationItem.prompt = "Welcome to the \(title) station!";Observe that the text in the
StationView
has moved down to accomodate the greater height of the navigation bar.
On iPhone 6,
it’s
74 pairs of pixels in portrait,
54 in landscape.
I acknowledge that the prompt is inane.
After you’ve seen it,
get rid of it.
viewDidLoad
method of class
StationController
.
(I would have preferred to insert it into the
init
method of class
StationController
,
after setting the
title
and
prompt
.
But at that early point, the
navigationController
property of the
StationController
is still
nil
).
navigationItem.rightBarButtonItem = UIBarButtonItem( title: "Go East", style: UIBarButtonItemStyle.Plain, target: navigationController!, action: "next");Even better, prevent the last station (Bedford Avenue) from having a “Go East” button.
let lineController: LineController = navigationController! as LineController; if lineController.viewControllers.count < lineController.titles.count { navigationItem.rightBarButtonItem = UIBarButtonItem( title: "Go East", style: UIBarButtonItemStyle.Plain, target: navigationController!, action: "next"); }What would happen if you changed the above
"Go East"
to
lineController.titles[lineController.viewControllers.count]
?
You can now remove the
touchesEnded(_:withEvent:)
StationView
.
Also take a look at the various types of
UIBarButtonSystemItem
s.
int
method of the
LineController
,
after the call to
super.init
,
set the
toolbarhidden
property of the
LineController
to
false
.
toolbarHidden = false;The tool bar that appears at the bottom of the screen will be empty. We will use a tool bar here.
LineController
,
should we have put it into a separate object called the model?
Then we would have all three components:
model/view/controller.
StationController
(and its
StationView
)
each time we retreat one station to the west,
and re-create the
StationController
(and its
StationView
)
each time we advance one station to the east.
We can verify this by adding the following method to class
StationController
.
(Actually, this method doesn’t get called.
I wonder why not.)
deinit { print("StationController deinit \(title)"); }It would save time if we could create the
StationController
the first time we visit it,
and keep it stored in an array in case we visit it again in the future.
Give the
LineController
the following property.
//all the stations we have already visited at any time var visited: [StationController] = [StationController](); //an empty array of StationControllersIn the
init
method of the
LineController
,
before the
pushViewController(_:animated:)
,
insert the following statement.
visited.append(firstStationController);Change the
next
method of the
LineController
to the following.
func next() { let i: Int = viewControllers.count; if i == titles.count { //We are currently visiting the last station, and can go no farther. return; } if visited.count <= i { //This station is being visited for the first time. //Create a StationController for it. let nextStationController: StationController = StationController(title: titles[i]); visited.append(nextStationController); } pushViewController(visited[i], animated: true); }