Lecture 3 - User Interfaces and Protocols and Delegates

Protocols and Delegates

If you've ever programmed in Java or C++, protocols are similar
to interfaces or abstract classes. If you don't have experience
with Java or C++ and that makes no sense to you, don't worry about
it though. Protocols and delegates are pretty easy to learn and
prove to be quite useful in iOS development.

Protocols and Delegates Playground

The protocols and delegates Playgrounds that we worked on in class
are fully annotated and can be found here,
including a partially realized example of the amazing Morbulator.

Protocols

We'll start out by covering the basics of protocols. First,
you're probably wondering what a protocol is. Well, it's kind of
just what it sounds like: some set of rules or plan for carrying
out something.

In the context of iOS development, a protocol is a set of
"rules" to which a class subscribes. So, by subscribing to a
protocol a class promises to do whatever is defined by the
protocol. This usually means that the class promises to implement
all of the functions and variables defined in the protocol. Let's
see an example to try and make more sense of all of this.

Pretend you have a farm with a bunch of animals. All of the
animals need to eat, right? That's a rule, so "eat" would be in the
FarmAnimal protocol. And each animal has a name, so "name" would be
in the FarmAnimal protocol too.

Different animals have different ways of handling these rules.
Let's say there's a cow named Otis. It's time for Otis to eat, so
since he's a cow he eats some grass. Now let's say there's a horse
named Pegasus. It's time for Pegasus to eat, so since he's a horse
he eats hay. See how it depends on the type of animal for how the
rule "eat" is carried out?

First, to declare a protocol (the set of "rules") you use the
keyword protocol and then whatever you want to name the
protocol.

Variables that you want to force as rules are declared the same
exact way that they are in classes. The only difference above is
the part that says get and set. This is basically to
say that this variable should be gettable (that is, other classes
should be able to read this member variable) and settable (other
classes should be able to set this member variable).

Functions are declared as stubs--identical to the way they're
written in classes, just without the function body.

So, classes that subscribe to FarmAnimal absolutely
must have a member variable, name, which is both
gettable and settable. They will also have another member variable,
food, which must be at least gettable. They must also have a
function, eat, which takes in nothing and returns nothing.
These are rules that classes must, must, MUST obey when they
subscribe to this protocol. Otherwise, Xcode complains and won't
compile the code.

Another thing to note is that when you define a protocol, the
protocol becomes a type. This type cannot be instantiated (it's
like an abstract class: the protocol doesn't have any
implementations...), but objects that implement the protocol can be
cast to the type, which is very helpful as we will see later.

So, let's show an example of a class that subscribes to this
protocol:

Now we create the Cow class which implments the
FarmAnimal protocol. See how the function and variables that
are delcared in the protocol are all implemented in this class? If
that wasn't the case, Xcode would complain at compile-time that the
Cow class does not properly implement FarmAnimal.

Now, we can have a totally different class subscribe to this
protocol:

The Horse class successfully subscribes to
FarmAnimal. It fits all the rules that are set by that
protocol, but does it in a different way to the Cow
class.

However, the Cow and Horse class have something in
common: they both subscribe to FarmAnimal. Therefore, we
should be able to, in some respects, treat both of these classes as
objects of the same type. We can do that by casting both of these
classes to FarmAnimal, like so:

So now observe that instances of classes which implement
FarmAnimal can be of type FarmAnimal. This can be
very helpful at times. Say you want to feed all your farm animals
at once. You can call all the farm animals a FarmAnimal, put
them in an array, and then just iterate over all the animals and
call eat. This is much easier and cleaner than feeding each
animal individually.

In summary, protocols are a set of functions and variables which
act as rules. When a class implements a protocol, it is promising
that it will implement all of the rules in the protocol.

Delegates

Now that we have covered protocols, we will talk about
delegates. To start, we'll discuss the idea of delegation.
Delegation is a design pattern. If you aren't sure what that means,
no worries. Basically, they are structures or outlines for
organizing code to solve common problems. In this case, delegation
is when an object hands off some of its work to another object.
That means you will have a class that contains a delegate object,
usually. To get a better idea of what this means, let's apply
delegation to the farm animal example.

So, for example, the Cow class had a "rule" that they had
to eat. Cow decided exactly how it would eat. But
realistically we know that Cows are kind of helpless and it
is actually your duty as the farmer to feed your Cows.
Therefore, you will help do some work for the cows by providing
them with food so they can eat. You are the Cow's
delegate.

So, first we will create a protocol to set the rules for what
our delegate will do:

protocol CowDelegate {
func feedCow(cow: Cow)
}

So, classes that implement this protocol will be responsible for
feeding a Cow. So now, we will create a CowFarmer
class that subscribes to this protocol and actually feeds the
cows.

We should see "Feeding Joe some grass...", followed by "Now I'm
going to take a nap because I'm a cow". Now, because
CowDelegate is a protocol, any class can subscribe to it.
Even a BadCowFarmer class like this:

There is so much wrong with that. But seriously take some time
and let this design pattern sink in.

In summary, a delegate is an object that does work for a certain
class. In Swift, it implemented with protocols in order to force
the desired behavior. This example with farm animals is pretty
simple, but protocols and delegates can be quite helpful when
designing an app. You will see this in your homework assignment
when you implement an add reminder feature to TinyReminders.

Intro to User Interfaces

The user interface is the point of contact between the
app and the user. In my opinion, it's the most important part of
your app, simply because a good or bad UI will make or break your
app. But, this isn't a design class, so we won't focus right now on
what makes a good or bad UI: what we're going to talk about is
how to build interfaces using Xcode.

In iOS development, there are three main ways to build
user interfaces: code, nibs, and storyboards. There's a decently
large debate in the iOS community about which method is the best
way to build user interfaces. All three methods have their
advantages and disadvantages, so we'll cover all three methods in
order for you as the developer to decide what works best for
you.

Storyboards

Storyboards are Apple's preferred way of building
interfaces. They're the way that you built the PhraseThrow app, as a matter of
fact! They provide the simplest, fastest, least-messy,
drag-and-drop functionality for build interfaces. Storyboards are
especially great for apps with multiple views (which are, pretty
much, every app ever, and also of which we have made zero, so far),
since they allow you to manage the transitions between those views,
called segues. Everything is managed in one file. An example
of a full storyboard might look like this:

Notice the multiple screens, and the transitions from screen to
screen. Storyboards map out the whole entire app for you.

However, Storyboards don't allow much in terms of customization.
Making very customized user interfaces (such as in an app like
Snapchat) tends to be very difficult and ends up leading into
coding your user interfaces, and wasted time. Also storyboards tend
to be bad with reusability and data flow.

Storyboards are the newest way of making interfaces, and is the
default way to make interfaces in Xcode 6.

Nibs

Nibs, or their more modern counterpart xibs, are
basically storyboards without the transitions. Basically, each view
controller you work with can come with its own specific screen that
you can drag-and-drop labels and buttons and whatnot on. Then, you
manage the transitions from screen-to-screen through code.

This is mostly a middle-of-the-road approach to user interface
building, since it's part drag-and-drop-abstraction and part code
(rather than Storyboards which involve almost no code, and is
all abstraction). This, of course, allows for a little bit
more customizability, especially with the transitions between two
screens. Nibs also solve the reusability problem that storyboards
have.

The biggest advantage of nibs is the ability to make custom
views for more than just full-sized screens. For example, table
cells, which we'll discuss today. These nibs can very easily be
integrated with your app. In fact, the app we build today will be a
mix of nibs and storyboards, combining the two easiest ways of
building an app interface.

The downsides of nibs are similar to those of storyboards: you
don't have total customizability yet. But it's a step above
storyboards.

Code

No abstraction. All code. You have access to every single
piece of every single view, and you have total control over every
transition. Total customizability exists in this realm.

However, as you can imagine, there are major cons to coding your
user interfaces: it's time-consuming, refactoring is hard, and you
won't know what your interface actually looks like until your build
and run your app.

This is my preferred way of building user interfaces, mostly
because you have the under-the-hood advantage: nothing is hidden
from you (has anyone had those "coding-compliant" issues using
Storyboards before? Yeah, those don't exist when you code
everything).

Short Intro to the Interface Builder

Today, however, we will focus on the first two methods of
building user interfaces: nibs and storyboards. To
use these two, we must use a very powerful built-in tool with Xcode
that you've already gotten fairly comfortable with: Interface
Builder.

Interface Builder is
a built-in tool for Xcode that allows you to make all of the views
for your iOS devices. It is descended from a piece of NeXTSTEP
software of the same name, and was released for iOS development in
2008. It's commonly regarded as the simplest way to make interfaces
your iOS devices.

You worked with Interface Builder in the PhraseThrow app, when you drag-
and-drop'd everything to your iPhone screen. That was all Interface
Builder. You were using Interface Builder to put views such as
buttons and labels on a blank iPhone screen, which you now know is
known as a nib, or alternatively, xib.

By the way, nib is short for NeXT Interface Builder. Xibs and
nibs are used interchangeably--a xib is a nib, but saved to file in
a different format (specifically XML), which makes things easier on
things like Git, for example. Nowadays Xcode uses xib exclusively,
but most people will still refer to it as a nib.

So, a single iPhone (or iPad) blank canvas is known as a nib.
Recall that a storyboard, is basically a bunch of nibs, and
their transitions (known as segues), shmushed together into
one giant graph-like structure. It is an abstraction of your iPhone
app that allows you to visualize how your app "flows".

TinyReminders

Today, we'll be building a very, very simple app called
TinyReminders. Simply said, it's an app that keeps track of
different reminders and dates in a table. You can view individual
reminders, and you can add new reminders. This will be our very
first multi-view app. Today, however, we will only implement the
table of reminders, and viewing individual reminders.

Designing the App

Let's talk about the design a little bit. There are basically
two views we want in our app. One is the main view that
displays all the reminders in a list. The other is the single
reminder view, which allows you to view the details of a single
reminder.

In general, it's a good idea to map everything out with pen and
paper first, so that you have a general idea of how you want your
app to like. Today, we'll map everything out on the blackboard,
that way we can all agree on what the app should look like.

Download The App

You can download the app here after the lecture. There will be
no online walk through tutorial on how this app was made.

Table View Controllers

We can use a Table View Controller to display information
about our list of reminders. What does a Table View Controller look
like? Open up your Contacts app. That's a Table View Controller,
listing all your contacts. Open up your Settings app. That's a
Table View Controller, listing all the different settings you can
change. Table View Controllers are just there to list
things.

Table View Cells

Each "list-item" in a table view controller is called a
TableViewCell. These cells are entirely customizable. This
is why the table in your Contacts app looks different from the
table in your Settings app, and they all look different from the
table in your Reminders app (which actually has a button built in).
In general, to make your favorite Table View, you configure a
TableViewCell, and then tell your TableViewController how to use
that Cell.

This is done most easily with Storyboards. In Xcode, you can
drag-and-drop a TableViewController into your Storyboard. Then, you
can add labels, buttons, and whatnot into the Prototype Cell
field, like so:

Ignore the title bar at the top. Your app will not have that by
default.

You can even adjust the height of the TableViewCell. But,
basically, what you have above is the prototype for what each cell
in the TableView will look like. The final step is to make a class
which subclasses UITableViewCell and set the Custom
Class for your prototype cell to be your new class. If you
click on your protoype cell (be careful not to click one of your
labels), you should be able to see on your right hand side:

Under Custom Class, put the name of your subclass of
UITableViewCell. Now, finally make the connections
(control-click) from the UI to your new class. This way, you have a
fully connected and implemented class that corresponds to that
specific UITableViewCell. Congrats! That was easy.

While you're modifiying your UITableViewCell, go
here:

The reuse identifier is an important identifier which
we'll talk about in a few paragraphs from now. For now, fill it in
with something handy like "reminder" or "contact" or "message" or
something descriptive.

Now, similarly, you're going to want make a custom class for
your UITableViewController. Do the same thing as above:
make a new class which subclasses UITableViewController,
and set the Custom Class for your table view controller to
be your new subclass. Now, you can customize your Table View
Controller!

The Cell Queue

Suppose you're really popular and you have 9,000+ contacts.
Loading all of those contacts into the table view controller will
legitimately murder your app with out-of-memory errors--you'll be
making 9,000+ UITableViewCell's!

To solve this problem, the smart engineers at Apple figure out
that, at any given point in time, you only really need around 10 or
so average-sized UITableViewCell's--about 6 or so are
displayed on the screen, and the rest are off-screen ready to be
displayed at a moment's notice.

So, these 10 or so cells are the only cells used, in tandem with
a reuse queue. Basically, when a cell goes far enough off
screen, the cell is put in the reuse queue, since it's not needed
at the moment. Then when a new cell is supposed to come on-screen,
you can specify to the UITableViewController that you want
to dequeue a cell from the reuse queue and repopulate it
with the necessary information. This way, you don't need to make a
bajillion cells--just a few.

One last problem is that your app uses several different reuse
queues. If your app uses multiple tables, how will it know which
queue it should dequeue from? Remember the reuse identifier I
mentioned a few paragraphs above? That's how. We'll see a code
example in a little bit which ties it all together.

Displaying the Cells

Table View Controllers required a data source in order to
display information. On a very basic level, a table view controller
requires two bits of information in order to display a list of
stuff: how much stuff is there, and, at a given position, what
information should be displayed there (i.e. what should be in the
first cell, the second, etc). Any class that can answer these
questions for the UITableViewController can subscribe to
UITableViewDataSource.

Classes that subscribe to UITableViewDataSource should
minimally implement the following method:

// This function is called when the table view controller wants to know
// what information should be displayed at which cell in the table (i.e.,
// what goes in the first, second, ..., nth cell, etc).
//
// Passes in something called an index-path. Basically, if you access
// |indexPath.row|, you will get the number that the table view controller
// is looking to get information for.
//
// Notice that this function actually *returns* a UITableViewCell.
//
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
}

Suppose your information is kept in the form of some array of
Objects. Then, one implementation of this function could
look like:

Quick and easy: get a cell for reuse (or make a new one), get
the object, transfer the data, return the cell. There's one more
function that needs to be implemented for the Data Source to work
well with the table view controller. The table view controller
needs to know how many cells you have! You have to implement the
following function:

This function passes you an argument called section and
wants to know how many rows are in that section. Table view
controllers can be sectioned! Look at your Settings app. It's a
table view controller which comes broken into several different
sections. This function passes in a section number (0, 1, 2, ...)
and wants to know how many cells are in that section.

For an app like TinyReminders, there aren't sections--just one
huge table. So, one implementation of this function could just be
to give the size of all your data. For example:

Navigation Controllers

What's the difference between the following two views?

The one the right has a title bar, of course. You've seen that
title bar before! Grab your iOS device and open up the Settings
app. The title bar is always at the top of that app. It has
different titles, and sometimes it has buttons on the top-left and
top-right corners. This title bar at the top helps you
navigate your app, doesn't it?

The title bar is called a UINavigationBar, and it's part
of a special view controller called a
UINavigationController. A UINavigationController is a
special class because it's known as a container class, which
basically means it contains other view controllers.

Whoa, okay, this is a lot being thrown at you. Let's approach it
slowly. Have you ever seen the movie Inception?

That movie is about dreams within dreams within dreams, several
levels down (and also Leonardo DiCaprio NOT winning an Academy
Award). Well views are the same way! You can have views inside
views inside views. Think back to PhraseThrow: the labels and
buttons were views inside a bigger view (the screen). We won't get
into this too much right now, but views have hierarchy and can be
nested.

A container view is just a view who doesn't do much
except contain another view. So, the UINavigationController is a
class that is mostly concerned with displaying another view, called
its root view controller. It does some other stuff, like
putting up a title bar, and maybe having some buttons, but it
doesn't do anything else besides displaying another view.

So, in the above example, the image on the left is just one
view. The image on the right is a view inside another view.
Specifically, the image on the right is the view on the left
inside a UINavigationController. Hence,
UINavigationController is a container.

The Navigation Stack

Now, of course, navigation controllers should be able to provide
navigation. In a navigation environment, new views are provided to
the screen in a stack-manner. What does this mean?

Again, open up your Settings app. Suppose you hit General. A new
view is pushed onto the stack, and a bar button item
appears in the top left corner of your screen that says "<
Settings". If you tap on that button, your view will be
popped off the stack, and your previous view will take the
forefront. Makes sense?

Navigating

Using Storyboards, let's add a second view to our app. Just drag
a View Controller over from our collection of view items. Then,
make a class that subclasses UIViewController and connect
it to your new view controller (just like you did with the
UITableViewCell and UITableViewController). Now,
you can decorate that view controller however you look and tie it
into the code for your new UIViewController subclass.

But now, suppose you want your second view controller to appear
whenever someone taps a cell on your table view. Storyboards make
that easy! Just right click on your prototype cell, click on the
circle next to "selection" and just drag it over to your second
view controller. You'll see three choices: modal, push, and
custom.

A modal view is a temporary view that requires user
attention. This is like what happens when you hit the New Tweet
button or something like that. It pops up and you can't navigate
out of it until you're done interfacing with it (like hitting a
Cancel button). Selecting push will simply push the second
view controller onto the navigation stack, just like we had
previously discussed. We won't worry about custom segues. So,
that's it! You made your first segue!

Bar Button Items

You can alternatively navigate using bar button items on
your Navigation Bar--the top bar that appears when you use a
UINavigationController. From your collection of view
items, find the bar button item, and drag it to the right hand side
of your navigation bar.

Now, you can play around with it. And modify it however you
like. When you're ready to connect it, go to the Connections
Inspector and under Triggered Segues, you should see a field called
``action". Again, drag the circle over to the view you want and
select modal, push, or custom.