In every app so far,
the
view
controller
has had a
view
underneath it,
i.e., under its supervision and control.
The view controller created the view in
loadView()
(although we often didn’t need to write this method of the view controller
ourselves),
triggered calls to the view’s
drawRect(_:)
method,
and contained a property named
view
that referred to the view.
A special type of view controller (i.e., a subclass of class
UIViewController
)
called a
UITabBarController
can have another view controller underneath it.
In fact, a
UITabBarController
can have a whole
array
of view controllers
underneath it,
each with its own view.
At any given moment, however,
only one view controller in this array can display its view on the screen.
The
selectedIndex
property of the
UITabBarController
tells us which view controller in the array is currently displaying its view.
UITabBarController
lets us switch back and forth in any order
between the view controllers beneath it.
Each of these other view controllers has a view of its own.
Our example has five view controllers under the tab bar controller,
one for each borough.
For simplicity, all five belong to the same class,
but they could belong to five totally different classes.
See the iOS Clock app for an example.
Keep the red badges short,
otherwise they will block the icons.
If a tab has an icon,
the badge will be in the tab’s upper right corner.
If the badge has no icon
(i.e., if
tabBarItem.image == nil
),
the badge will be in the upper left corner.
UITabBarSystemItem
for a list of Apple’s standard icons.
UIViewController
.
UITabBarController
.
(A future example will also need protocol
UITabBarControllerDelegate
.)
AppDelegate
.
The
application(_:didFinishLaunchingWithOptions)
method of the application delegate
creates an array of
ViewController
s
and puts it under the tab bar controller.
ViewController
.
Its
init
method receives parameters from the
ApDelegate
.
Its
loadView
method passes a parameter to the
init
method of its view.
View
Images.xcassets
,
an Xcode
asset catalog
file.
Apple prefers
.png
format.
Bronx_unselected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Bronx_unselected image set.Bronx_unselected@2x.png
:
one of the files belonging to the Bronx_selected image set.
60 × 60 pixels.
Bronx_selected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Bronx_selected image set.Bronx_selected@2x.png
:
one of the files belonging to the Bronx_selected image set.
60 × 60 pixels.
Brooklyn_unselected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Brooklyn_unselected image set.Brooklyn_unselected@2x.png
:
one of the files belonging to the Brooklyn_selected image set.
60 × 60 pixels.
Manhattan_unselected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Manhattan_unselected image set.Manhattan_unselected@2x.png
:
one of the files belonging to the Manhattan_selected image set.
60 × 60 pixels.
Queens_unselected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Queens_unselected image set.Queens_unselected@2x.png
:
one of the files belonging to the Queens_selected image set.
60 × 60 pixels.
Staten_unselected.imageset
Contents.json
:
a
JSON
file listing the files belonging to the Staten_unselected image set.Staten_unselected@2x.png
:
one of the files belonging to the Staten_selected image set.
60 × 60 pixels.
We have to tell the app that the first view controller it creates should be of
Apple’s class
UITabBarController
,
not of our class
ViewController
in the file
ViewController.swift
.
Select the
Main.storyboard
file 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.
.png
Images.xcassets
file of the project.
See
America.
For iPhone 6,
each icon is 60 × 60 pixels and has a name that ends with
@2x.png
.
I should have created additional icons without the
@2x
for older, non-retina devices.
I created the icons with
this Unix shellscript,
but you probably have a better way of creating icons.
Or you can get them from many places, including
Glyphish.
The tab bar controller ignores the colors of the pixels in the icons.
It pays attention only to the alpha level of each pixel.
Pixels whose alpha is 0 are invisible;
those whose alpha is 1 are drawn.
In
Bronx_unselected@2x.png
,
for example,
the pixels along the edges and diagonal have alpha 1
and all other pixels have alpha 0.
To make it possible to examine the
.png
file,
I colored the pixels along the edges and diagonal black,
and all other pixels white.
But the tab bar controller ignores these colors.
The
@2x.png
s
are not mentioned in the
application
delegate,
but the correct files are opened anyway,
beased on whether the device is retina or non-retina.
The tab bar is 49 pairs of pixels high on iPhone 6.
The image in each tab bar item must be a 60 × 60 pixel
.png
file
drawn with the
alpha channel only.
I created one with the Adobe Photoshop CS4 Extended version 11.0.1 at NYU.
File → New… Name: bronx Width: 30 pixels Height: 30 pixels Color Mode: RGB Color Background Contents: Transparent OK
You now have a square file with a white and gray checkered background.
It should be displayed in two places:
in a little
bronx @ 100% (Layer 1, RGB/8)
window,
and in the
Navigator
tab.
Select the pencil tool. Resize the pencil if necessary. The lines you draw will appear in blue against a gray background when the item is selected, and in gray against a black background when the item is not selected.
File → Save As… Format: PNG Save PNG Options Interlace None OK
Then add the png file to the
Images.xcassets
file of your Xcode project.
//immediately before the return true; at the end of the //application(_:didFinishLaunchingWithOptions:) method of the app delegate let tabBar: UITabBar = tabBarController.tabBar; print("tabBar.frame = \(tabBar.frame)");
iPhone 6 in portrait orientation:
tabBar.frame = (0.0,618.0,375.0,49.0)
On the other hand, people get confused when they see a tab bar controller with less than three tabs. If you have only two views, you might want to do this instead.
Bronx_unselected@2x.png
and
Bronx_selected@2x.png
.
Observe that they are photographic negatives of each other:
when you select the Bronx, it turns into two blue isoceles right triangles.
Brooklyn has only one icon,
Brooklyn_unselected@2x.png
,
identical to
Bronx_unselected@2x.png
.
When you select Brooklyn,
the icon merely changes color.
drawRect(_:)
method of the currently displayed view is called
when the orientation of the device changes.
And the above screenshots show that the same is true for the present app:
the width and height numbers are swapped.
But what happens if we comment out the assignment to the
contentMode
property in the
init
method of class
View
?
See
ContentMode.Redraw
vs.
ContentMode.ScaleToFill
.
init
method of class
ViewController
to the following.
init(title: String, unselected: String, selected: String?, badge: String?, text: String) { self.text = text; //self.text is the property, text is the parameter super.init(nibName: nil, bundle: nil); if title == "Staten Island" { tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.Contacts, tag: 0); } else { self.title = title; tabBarItem.image = UIImage(named: unselected); if selected != nil { tabBarItem.selectedImage = UIImage(named: selected!); } } if badge != nil { tabBarItem.badgeValue = badge!; } }
Main.storyboard
file and look at it.