>> Stanford University. >> Welcome to Stanford CS193P, Developing Applications for
iOS, winter of 2017. This is lecture number five? Is that right?
The lecture number five? Yeah, lecture number five. Today we are going to start
off with a little demo, where I'm gonna take the
application we worked on last time, and add a model to it. Cuz we have only worked
on the view part of that, building that nice
cool FaceView, now we are going to add
a model to that MVC. Then we're going to come
back to the slides and I'm going to do some
slides on gestures. Obviously, you want to be able
to have input to your app, you need to know how to
do multi-touch gestures, we'll talk all about that, and then I will go back to our
FaceView application and we will add some gestures,
pinching, panning, tapping. And finally, I will go back
to the slides again and start talking about
multiple MVCs. How to build bigger
applications that are made out of combining MVCs, so that's
what's on tap for today. So let's jump right back
into this FaceIt Demo. And if you remember
where we left off, we had this nice view,
it was really cool because, in Interface Builder, we could
do things like open the eyes. We could make it really big, we could make it really small,
or anywhere in between. And we could change the color,
green, something like that,
so that's cool. And this is a nice reusable
thing that's got a little public API to it. These inspectable
things are all public, so, we can set the mouth
curvature, we can open and close the eyes, we can change
those things, like scale. And so, it's nice,
it's this nice reusable face. And what we're going to do is
make our MVC have a model. That model is not gonna
match up exactly to what our FaceView can do, because
that's the way models work. Models in the world are often
databases or something, their schema doesn't quite match
what view you have around. So you, the job of
the controller is to interpret that model for
the view as best it can. And interpret input from the
view also to affect the model. So let's go back
to our controller, you can see that we have no
model, we also have no outlets to our view here, so, let's
add both of those things. I'm gonna start by adding
an outlet to our FaceView, so remember how we do that, we
got our storyboard on screen. I'm gonna get them both on
screen at the same time and then I'm gonna control drag. And I'm control dragging
from my FaceView to create an outlet to it, in exactly the same way I
would control drag to a label, or button, or
anything else, so nothing special
about the FaceView. So I'm just gonna hold down
control and drag in here, and I'm gonna
create an outlet, I'll call this
outlet my FaceView. That's a perfectly
good name for it. Hit connect and there we go,
we get this code that you're quite used to here, which
is this IBOutlet weak var. Now we have a connection to
our view, and remember, in MVC picture, the controller can
have green arrows to its view, that's this, and it can have
green arrows to its model. So let's create
a connection to the model. So what is gonna be
our model of our MVC? I'm actually going to
drag that in, right here, it's called facial expression. When you drag things in
I strongly recommend you have this Destination,
Copy items if needed, otherwise, when you drag in
a class, or structure, or something, you'll
be pointing to it. So if you change it in,
wherever you got it from, it will be changing
it in your project, so, it's usually
what we wanna do. Maybe that might
be what you want, in certain circumstances, but
usually wanna copy it in. So let's take a look at this
thing that I brought in, it's a struct and it's gonna
be the model of our FaceView. And it's a really simple
FacialExpression, that's what this represents,
this struct. And it has positions
of the eyes, open eyes, closed eyes,
squinting eyes, unfortunately, we can't represent that in our
view but it's in the model, we'll have to figure
out the best we can do. It's also got a mouth, and the
mouth can frown or smirk, or it can grin, or smile, notice there's nothing in this
model about mouth curvature. It doesn't know anything
about mouth curvature, it just knows about grins and frowns, things like that,
that's what the model is. It's also kinda cool, it knows
how to give you a happier version of itself, or
a sadder version of itself. That's what these bars do, they just wanna return
a happier or sadder. But primarily, this struct is
just an expression to keep track of the state of
the eyes and the mouth. So I'm going to,
in my controller, create a green arrow to it,
or a var for it. We're gonna call
it Expression, cuz it's a facial expression. And it's type
FacialExpression, and actually, let's have a starting facial
expression have eyes, which are, let's say, open,
and a mouth, which is a grin. That sound like a happy
place to start. So that's great, now, remember that the primary
job of a controller is to interpret the model for
the view and vice versa. So I'm gonna add
a private function here- you'll all often
have a method or a number of methods in
your controller like this. This one I'm gonna
call updateUI. And the job of this
method is just to make the model match the UI,
tha's i's job. Tha's what this
method is gonna do. So, what do we have to
do to make our model, this facial expression,
match our UI? Well, we have to deal
with the eyes and we have to deal
with the mouth. So, let's deal with the eyes
first, that's pretty easy, so, I'm just going to switch
on my expressions.eyes. And in the case that the eyes
are open in the model, then it's easy, I can
just take my faceView and set it's eyesOpen to true,
that's obvious how we do that. And this face, you remember,
is this outlet, that I just created there,
and so, that's obvious. How about the case where the
eyes are closed in the model? Well, that's
the faceView.eyesOpen, are gonna be false. And finally,
there's this case in the model where we're squinting,
and here, I don't really have
a way to represent that. So I'm just going to say
that in that case our eyes are gonna be closed because,
squinting is more like having your eyes closed than
having them open. But we don't always have
exactly the view we want to represent our model, so we're
doing the best we can here. So that is the eyes,
pretty straightforward. Now the mouth,
I could do the same thing for the mouth here,
case expression.mouth, if it's a grin, set
the mouth curvature to this. If it's normal, set
the mouth curvature to that, but that code is
kind of messy, (make some space here so
it's up higher), what if I created a little private var
here, actually make it a let. And I'm gonna call it
mouthCurvatures, and it's gonna be a dictionary,
and in the dictionary, the values are gonna be
FacialExpression.MouthPositi- ons, maybe a grin. And the value is gonna
be a mouth curvature, so what's a good thing for
a grin? I think I decided 0.5. So this is just gonna
be a dictionary with mouth position in the model,
mouthCurvatures for the view. So I'm just creating this
little mapping data structure. So let's do this for
all, let's map this. For all of our facial
expressions right here. Notice, by the way, that Swift was able to infer
the type of this dictionary. Cuz it only has one item in
it, and the types of those two things are obvious
from what they are. So now that it knows that,
by the way, we can be a little more
succinct and just say like, frown is -1.0 and
smile is 1.0. And obviously neutral
would be 0.0. Did I get them all? Smirk, how about smirk? Nope, that smirk
we'll say is -0.5. So I've just put those mouth
curvatures in this nice little table. And so now in my update UI, I'm just gonna set my
faceView's mouthCurvature equal to looking up in
the mouthCurvatures, the model's mouth. I just kinda wanna do it this
way to kinda show you that you don't always have to
forcefully case every single thing when you have an enum. Sometimes you can use
things like this. Now, I have an error there, anybody know why
I have an error? Can anyone guess? Optional, yeah. Alright, people are getting
the optional thing. Good.
MouthCurvature, what type is that? That is a double, and
mouthCurvature sub expression.mouth, what
type is that? It's an optional double. And that's because this
expression might not be in that dictionary. So I'm gonna use that
defaulting thing we learned and say, if I can't find
the expression mouth, we are gonna have neutral
mouth, not smiling or frowning as our curvature. Now, we have this
great update UI. When should we
call this thing? Well, we certainly need to
call this thing anytime our model changes. If our model changes to have
a different expression, we gotta update our UI. So how are we gonna do that? We're gonna use this cool
feature that I talked about last time called property
observers, this didSet thing. So didSet allows you
on any of your vars, if it changes,
you can execute some code. So anytime someone
sets this expression, I'm just going
to update my UI. Now, there's one other
place that I wanna update my UI though, it's
a little less obvious, and that is with this
property observer. What am I doing here? This property is the outlet, that thing I Ctrl+dragged
to my faceView. And when we first start up,
it's not set. iOS takes a few microseconds
to actually connect this up. That's why this is
an optional, remember? In that short amount of time,
what if the model gets set. Then update UI is not gonna
work because the faceView hasn't been connected yet. So that's no good. Also we initialized our
FacialExpression to be this. When you initialize something,
didSet does not get called. didSet is only called if
someone externally sets the expression. The initial thing does
not cause it to be set. So when is didSet
gonna happen? It's gonna happen only once
when iOS hooks up this outlet to that faceView, then this
didSet is gonna get called. That's the only time it's
ever gonna get called. But at that time,
we're gonna update our UI. Now, there's one other
minor thing here. What if this gets called
before this happens? This is gonna
crash our program because until this gets hooked
up to iOS, this is nil. The only reason we don't have
to say exclamation point here is cuz we have this implicitly
unwrapped optional here, but it is an optional. So how can we deal with that? We don't want update UI
to crash our program if the expression should
happen to be set before that thing gets wired up. Now that thing gets
wired up pretty soon, so probably not likely to happen,
but it could. So, we're gonna do that
by using this chaining, this optional chaining. Remember the optional
chaining, where you can put
a question mark there. And if the thing that you put
the question mark on evaluates to nil, the rest of that line
of code is just ignored. Perfect, faceView?.eyesOpen,
if faceView's not set, we'll just ignore that. We're not gonna set
the eyes open to anything, that line is just gonna
be aborted immediately. This is something you often
wanna do in your update UI things is protect against
your outlets not being set And it's all gonna be fine,
because if this got sent, and this has not been, and
then this happens, and none of these lines of
code execute, no problem. Because when this
does get hooked up, this is gonna get
called again. And this time,
it'll be able to do it. So that it. Let's run our
little app here and see if we get
an open eyes grin. In other words, let's see if our UI reflects
what's in our model. And sure enough,
open eyes and a little grin. Let's try closed eyes, closed and a little frown. Bingo! So now we have done what we
need to do in our controller, which is we're interpreting
our model for our view. And anytime we set
the model from now on, it's gonna update our
view automatically. So the question is, what if
I forgot to put this little question mark right here? Probably everything would be
fine because you don't set your model before that Outlook
gets wired up by iOS cuz it wires it up really soon. But if you did, And so
that's how you would know, "oops", you messed that up. And sometimes you'll forget to
put that question mark there, You do all your coding and you
never call that model earlier. it's fine. But then later, you change
your code in a way that does accept the model before
that thing, and boom. It crashes, and
you find it right away. That's why crashing is not always the worst
thing in the world. Crashing helps you
find bugs like that. You just hope you
don't ship it out, and the customers find that
bug for you that way. So that's it, that's all
I wanted to do is show you now we have
a complete MVC. It's got a model,
it's got a view, it's got a controller that is
interpreting for each other. So we can go back
to our slides and learn about gestures, and then we'll add some
gestures to this thing. So, how do we get
those touches? Now, it is possible
to get notified of raw touch events like
a touch, meaning a finger, went down on the screen,
it moved, it went up. Two fingers went down, you can find out all about
that, but we never do. Too complicated, trying to
figure out whether this, down, swipe, up is a swipe versus
just moving, that's too hard. So we're gonna let
iOS do that for us. And we call that interacting
with multi touch, touch via gestures. So we're not even gonna
learn about about the raw touch input in this course,
just gestures. So gestures are recognized by this class called
the UIGestureRecognizer. Actually, not by
UIGestureRecognizer itself, that's an abstract class. How many people know what
an abstract class is? Raise your hand if you
know what that is. Okay, so
I'll take a second to tell ya. So an abstract class is
a class that, it's only purpose in life is to be the
super class of other classes. And you never actually create
an abstract class, okay? You only create
subclasses of it, instances of subclasses of it. So UI gesture recognizer's
abstract, you would never create a UI, you'd never call
an initializer on UI gesture recognizer. Instead there's a bunch of
subclasses which I'm gonna talk about and you would call
the initializer on them. Now there's two parts to using
a gesture recognizer here. One is you have to ask
a UIView, please start recognizing this gesture, like
a swipe or a pan or whatever. And then number two is, when
the recognizer does recognize it, it wants to call you
back with a handler, what we call a handler,
a gesture handler. So there's the recognizer
that you add to a view and then there's the handler,
anybody can be the handler, as you'll see. Almost always it's either the
view itself or a controller, but anybody could be it. Now the adding of the gesture
recognizer is done with a method called add gesture
recognizer in UIView. And that method is usually
called by controllers, why do controllers call that? That's because a controller
wants to control whether the things in its view
recognize certain gestures. They might wanna turn off
certain gestures or or not turn on certain gestures,
so the controller is often
controlling that. But occasionally you do
get a UIView that will add a gesture to itself,
like scroll view. Because a scroll view
without the pan gesture, or without the pinching
gesture if it can zoom, it's not even a scroll view. In other words,
that gesture is so fundamental to what a scroll
view is that scroll view adds It'd, you'd have some API to
turn it off, but it adds it to it to itself. itself, it doesn't make some
controller add this gesture, it just recognizes pan and
pinch. So mostly it's done
by controllers, though you'll see that. The second thing, the handler, okay that could either be
the controller or the view. And what it really depends on
is whether the gesture affects the model, or whether the gesture affects
just the view, okay. If the gesture only
affects the view, then the view will
probably handle it. But if the gesture affects
the model directly, now the controller has to handle it because a view
can't see the model. So the handler is either
controller view and not, and you'll see that in the demo. I'm gonna have one handler
that's a model, one and one that controls view and
you'll see where we put them. Alright, so what's the code
look like to add a gesture recognizer to a view? So for my example here, let's say you had a view,
and in this code I'm gonna put here by the way is
gonna be in a controller. Let's say it has one of its
views that it wants to add a pan gesture, a pan gesture
is put your finger down on the screen and move around. That's called panning
around in the view. So this is what it looks like,
a good place to add a gesture recognizer is in the didSet
of an outlet for that view. Because that's, as soon as you
can get a hold of it anyway, your iOS just sets,
set that outlet up for you, so now you've got
a hold of that view. Now you can send
a message to that view, addGestureRecognizer. Here I have this var, this
outlet called pannableView, this is the view that I wanna
add the pan gesture to. And you can see
that in its didSet, in other words when
iOS wires this up, I'm going to do this
addGestureRecognizer. First I'm gonna create
the gesture recognizer, notice I'm saying
UIPanGestureRecognizer, in it, in the initializer for that. Not UIGestureRecognizer,
I'm creating a concrete gesture recognizer
which is for panning. And that it has two arguments,
the both of these arguments are the handler,
how this thing is handled. The target is who's
gonna handle it, and the action is what method
is going to handle it. So now in the case here,
the target, I'm having the target will be
myself, the view controller. This is obviously in the view
controller because this is in an outlet property observer, that would only make sense
in a view controller. And so the target is myself,
I'm going to set, ask when this pan gestures
starts to be recognized it's gonna be sending me
the action here to handle it. And the action, you notice
you have to say #selector to specify the method. Don't get confused the
difference between a function type, a type that
is a function and an actual method in a class. We use #selector to
say this is a method, I wanna call this specific
method in my class. And notice I've said
ViewController.pan( recognizer:), well that means
that the method that's gonna be called is called pan. And it has one argument, whose
external name is recognizer: that's what that means. The ViewController., means
that method is in myself. If it was in my view it
would probably be like PannableView.whatever. This could also be self.pan,
that's allowed, saying pan.panGesture,
pan recognizer in myself. Also you could omit it,
that part in totally and just say panRecognizer:
because the default is for it to be in myself. So there's three ways I
could have specified that, viewController.pan, self.pan,
or just pan. But notice it does have
that argument by the way, that argument is gonna be the
recognizer passed back to me. So when the pan starts to be
recognized, it's gonna call this method, and that
recognizer is gonna be passed. This pan gesture recognizer
called panGestureRecognizer right there, that's gonna be
passed back to me each time. Now I've got a gesture
recognizer for panning, I just add
it to the view, only views are capable
of recognizing gestures. So if you have
a gesture recognizer, the only addGestureRecognizer
method in the entire UI kit is in UIView, so you have
to send it to UIView, or a subclass of UIView. Let's talk about the handling,
so now this is code, adds the handler so that the view is gonna start
looking for pan gestures. And then when it finds them, it's going to start calling
this pan recognizer. Now before we can
look at the code for that we gotta understand
a few things about it. One is that when you get
a gesture like a pan or a pinch you need some concrete
subclass specific information about it like a pan. You gotta know where it is,
or a pinch, you gotta know
much you pinched. So we have to understand
what that means, now for a pan gesture
recognizer, there are three. It's actually a little more,
but there's this three very important pieces of concrete
information about the pan. So, translation(in: UIView) is
a function in the pan gesture recognizer where
you can ask it, how far has this pan
moved since it started. How far has it moved from the
start as I'm moving around, x and y, it's a CG point,
so it's giving me the x and y where it's moved
to since it started. Velocity says how fast
is the finger moving. Is it kind of
whipping it around or is it going really slow? Because if it's
going really slow, maybe you wanna do a very
you know, small drawing. And if he's whipping
it really fast and you just wanna you know, moves
something around, who knows? And then there's
setTranslation, now this last one is
kinda cool because if you just know the start
point and where it's moved to. You're not really getting
the incremental move with each time you update, so you can actually set
that translation to zero. And then instead of getting
the distance from the origin, you're getting from the
distance from the last time this was sent to you. Cuz this handler's going
to be sent to you over and over as the pan moves
around repeatedly. Hundreds of times as you
move move around, so often you'll wanna set that
translation back to zero, it gives you
incremental panning. You'll see that with some
of the other concrete Now, the abstract superclass,
the UIGestureRecognizer, ones as well. even though you don't make one
it still is the superclass of these things. And you do inherit, especially this one very
important var, Called state. And a state tells you
the state of the gesture's, how it's being recognized. So all of these gesture
recognizers start out in the state possible. It's possible this
gesture could happen. And then, once it starts if
it's a continuous gesture like a pan or a pinch you
go to the state began. So this isn't like, " a pan!,
I think it's started". So you'll get your handlers
called and you can look at the state of the recognizer
and say, it began. And then,
every time the finger moves, it's gonna get changed. It gets change, change,
change, change, change, change, change. You're getting your handler
called repeatedly and no anything that's changing
is those methods above, translation and
also the state. Moves to change. And then, when the finger
goes up, you get ended. You'll get your handler called
and the state will be ended. Now, for a discrete
gesture like a swipe, a swipe either happened or
not. When you're looking at
a swipe you don't get the swipe started and it's
moving across the screen and it comes off, no. Either the swipe happened or
not. So you would just
get called once and the stage is gonna go straight
to being ended or recognized. But you have to be a little
bit careful of the states failed and cancelled. Failed might be because
you started a gesture and then the system realized,
my gosh, that wasn't a pan gesture,
that was the start of a swipe. Or something like that. And so,
it switches to a swipe. So you could have canceled,
and then the other one, or the failed, and then you
also didn't have canceled. You're in the middle of
panning, phone call comes in, takes over your screen, person
says, hello, how you doing? Well, clearly,
that pan gesture was canceled. So you could get that,
as well. So if you ever do anything,
when you gesture ends, then you have to be careful
to clean up when it fails, or gets canceled, as well. So that's the state. So let's take a look then,
knowing what we know about pan gestures, what the handler
would look like. So remember that
the handler is called pan, recognizer is the argument
there, and that is the pan gesture recognizer itself
coming back to us. And its state will
be different, and we can ask it about
translation and view and all those things. So the first thing we're gonna
do is look at that state and I'm going to do the same
thing whether the pan is, has changed or
whether the finger went up. That's kinda the last
position of the pan, we're gonna do
the exact same thing. Notice by the way I'm using
this switch thing fallthrough, hopefully you got that in
your reading assignment. That basically just means that
you wanna fall through to the next case cuz in a lot of
languages that's the default, but in Swift it's
not the default, you don't fall through. So fallthrough makes
you fall through, it's a special key word. You could also say
case.changed.ended: that would probably be
actually better. We'll do that in the demo. So if the pan moves a little
or it ends, then I'm going to get the translation from
where we started at first by asking for the translation
in the pannable view. That's the view the pan
is having there. You're almost always
gonna ask for the translation in
the pannable view. But you could ask for
it in other views and it will convert to that view's
coordinate system if you want. But almost always
you're asking for the one in the pannable view. Then, I'm gonna take that
translation that I'm gonna move and I'm gonna go do
something with my app. Draw a line to, if I'm tracking the finger
with the line, or if I'm moving something, I'm gonna move the thing
to the new position. Then, if I want, I can set
translation to zero so the next time this
handler is called, the translation is gonna
be just the distance from the last time it was called. As opposed to the distance
from where it all started. Where the pan started. So that's it. That's how a handler works,
it's easy. Let's talk a little bit about
some of the other concrete gestures. So I put my fingers down,
two fingers on the screen. Now, the scale is 1.0. If I put my fingers
twice as far apart, now the scale is 2.0. If I go down to half as
small now, the scale is 0.5. So that's what pinch is doing. It just constantly reporting
what the scale is, relative to what
the pinch started. Now, that scale is
not read-only var, so you can reset it as well. And then,
you'll get incremental scale. How much the scale has
changed since the last time? There's almost like you keep
resetting the initial finger down, to scale,
if you set that scale to one. That's how you can get
incremental scale. We'll do that in
the demo as well. And then,
you also get velocity. How many points for a second
or whatever this thing is, or not really a point fact. It's a scale factor. The percent of
scale is changing, how fast it's
changing per second. There's also a rotation
gesture that's two fingers down, like turning a knob. Keep the two fingers down. You turn them. You get the rotation there. The rotation is in radians. Again, it's not read only so
you could reset it and get the incremental turning. And you also get the velocity
and radians per second. How fast the person is
turning their knob. There's also SwipeGesture,
and again this is discrete so your handler's only
gonna be called once. If it recognizes this gesture. Now, because of that, you have to configure
a SwipeGesture recognizer. You create it and then you wanna set bars
here like direction. You wanna left swipe,
right swipe, up, down. And how many fingers? Two finger swipe? Three finger swipe? You set all that up when you
create it because then it's One finger swipe? just gonna be looking for
that. And when it finds it, it's
gonna call your handler once. Cuz it's discrete. Then there's tap gesture,
similar to the swipe gesture in that you have
to configure it. Number of taps,
like is it a double tap and is it two finger tap,
you do that. And then, it's gonna call,
this is not purely a discrete gesture, like SwipeGesture,
really you're looking for the state ended with
a tap as well, but still you have to preconfigure
it like a swipe, so it behaves almost exactly
like a discrete gesture. So hopefully this will make a
lot more sense when we show in the demo here, our demo we're
gonna add three gestures, one is a pinch, that's just
gonna make the face bigger and Which has nothing to do with
a model that view only so smaller. we're gonna put that
handler in the view. And then, we gonna do pan,
moving around. Actually no, we're gonna
swipe instead of pan. Let's use swipe. We do swipe and we swipe up
to make our face happier or swipe down to make it sadder. And we'll have tab to open and
close your eyes. How about that? And those last two
are obviously gonna affect the model, so the'll have to
be handled by the controller. So le's go back to our
code where we were here and how we can do this. Le's do the pinch first. Now, the pinch does't
affect the model, so it can be handled by the view. So le's actually go
over to our view and start by writing a handler for
a pinch. I'm gonna call this handler
func changeScale because that's what it's gonna do,
it's gonna be a pinch. So it's gonna change
the scale of our head. And I'm gonna say byReactingTo
a pinchRecognizer, Which will be UI
pinch recognizer, pinch gesture recognizer,
there we go. So this is gonna
be my handler. So it's gonna get this pinch
sent back to me all the time. I'm not gonna look at that
pinch of scale to know how much to scale
my own scale. So I'm gonna switch on
the pinch recognizer's state. Remember, it inherits that
from UI gesture recognizer, which is the super
class of this. And in the case that
it is changed, so the pinch has changed, or if it's ended, which means the
user lifted the fingers off. Neither of those cases
I'm gonna adjust my scale by whatever the scale is
of the pinchRecognizer. Simple as that. Now the thing about
this though, what if I start my pinch here 1.0, and
I move it just a little bit. Okay, fine. My scale gets moved
just a little bit. What if I moved all
the way out to scale 2.0? Now my face gets 2.0. Now what if I move
it out to 2.1? This code's gonna make my face
go from 2.1 to 4.2 you see. So I need to keep resetting
this scale back to 1 all the time so I'm getting
incremental scale changes. So I'm gonna say pinch
recognizer .scale = 1. So I'm just
constantly resetting. Every time this gets called,
and I move my scale up,
I'm gonna reset the scale so that I'm starting at one
again with my pinch. That gives me incremental
scaling there. And of course we have to
handle all the other cases of the state. But I'm not gonna do anything. So I'm not gonna do anything
when the pinch starts. If it gets cancelled,
I don't care. I'll just leave it
whatever scale it was in at the time, etc. So I don't need to really
handle any other cases. So that's it. The handlers are super
easy to write. And this scale by the way, everyone understands, is this
scale right here, this bar. This is our scale. Questions about that? So now we have a handler. We have someone to handle when
a pinch happens in the view. Now all we need to do is add
a recognizer that recognizes a pinch, and
calls the handler. So we're gonna do that up
here in our face views didSet, because this is the
first time we got a hold of that face view, and I want
the face view to be the one that is recognizing
this pinch. So let's start by
creating the handlers, so I'm going to create a local
variabilities to make it clear to my handler. It's a hashtag selector and
its the faceView method, changeScalebyReactingTo,
that's my handler. And then I am going to
create a recognizer here, a pinch recognizer and it's going to be
a UIPinchGestureRecognizer and of course we need
the target and the action. That's all we need to
create a recognizer. The target is going
to be the faceView, it's gonna handle this
thing itself, and the action is gonna
be this handler. That's it. Now we just need to
take the faceView and add GestureRecognizer,
the pinchRecognizer. And by doing that, face view is gonna start
recognizing pinches. And when it recognizes one,
it's gonna send this handler message right here
to the face view. Now, if you hope this is gonna work,
let's see. When you might ask,
by the way, if I have a mouse here,
how do I do a pinch? In my thing, okay? Well the magic
there is option. If you press option, and look, I haven't even
touched anything, see I have two
things right there. And they kinda stay
the same apart. You can also do option shift,
and they'll offset them, so you can pinch
anywhere you want. So anyway, do that. No, it's not working. See that?
How could that possibly be? Well, let's take a moment
here and debug this. Why would this not be working? I put that pinch gesture. I added it. I have a handler. Let's start by going
to our FaceView and putting a break point right
here in our handler Bar. And let's see if it's
actually handling it okay. So I'm going to
go back to here, I'm going to get my
option ready, pinch. Alright! It broke. It's definitely
calling my handler. So what's the problem? Let's do nothing. Let's say let's
print our scale and see if our scale
is being change. I'm just gonna put a little
print line in here. Use a combination of
break points here and doing print debugging. And let's, let's see if the scale is
actually being changed. Here we go. Again option key, and,
it looks like it is. I see something in
the background there. Sure enough look at that. Half, double,
it's working perfectly. Why is it not
redrawing that face. Can anyone thing why
it's not redrawing. Cuz we know it's working. It's because when you change
a var, like scale, right here it does not automatically
know to redraw your view. That scale is changing fine. But the last time draw was
called was when this first thing was drawn, you know,
this method we have down here in the FaceView
called draw CGRect. This thing? This was called when
it was first drawn and it's never been called again. So without this being called
there's no other way to draw my face I told you this
is the only way to draw. So we have to get
this to get called. And how are we gonna do that? Well we're just gonna slide
right up here to scale, and any time scale get called,
we're gonna do a didSet. Okay, and
inside here we're going to cause ourself to get drawn. Now how do I do that? Do I call draw like that? No, I see a lot of shaking
heads no and absolutely not. Of course we never call draw. We get huge trouble
if we do that. What do we call instead? Anyone remember
from the slides? SetNeedsDisplay. So we're telling the system
something about us has changed, we need to
be re-displayed. So please, at your earliest
convenience, re-display us. And that's what it's gonna do. And in fact, we want this
on all of our things here. Any time any of these public
things get changed by someone, we want to re-display. Let's put this
comment up here. So this is very common to have
this little snippet of code after any var,
that when you change it, would change the way
things look. Now when we run we can
actually go back here and get rid of this print. Pretty confident it's going to work now. Here I go on option again, and
sure enough, look, our scale. Since our scale changes here,
that setNeedsDisplay is asking to draw and iOS is
accommodating us by drawing. That's pretty cool, that was quite simple to put
in that gesture to do that. So let's go back now
to our controller, and think about doing a gesture
like a tap, to open the eyes. Now again, the view could
be the handler for that. It arguably knows how to
open an close its eyes. But if we made the view be
the handler for a tap, and it opened the eyes, it would get
out of sync with the model. Cuz the model is what says
whether the eyes are open, not the face view. Face view is a slave. It's a minion of
the controller, it's supposed to do what
the controller wants, so we can't do that. So we're gonna have to have
the controller handle a tap, and change it in the model. And then it'll automatically update in the UI because of
this line of code right here. Any time we change our model
it's gonna update our UI. So we're gonna just have
to have a handler for tap that just
changes our model. And that's all it needs to do. Let's try that handler first. Let's call that handler,
how about toggleEyes cuz we're gonna tap and
it's gonna with the eyes. If its opened it'll close,
close it'll open. And toggleEyes is
a tapGestureRecognizer thing, so I'm gonna say byReacting
to a tapRecognizer which is a type
UITapGestureRecognizer. And here I'm just gonna say, if the tapRecognizer.state
is ended. So the tap happened. Then I'm gonna let eyes, which
is FacialExpression.Eyes, just so that you know
what type that is, equal whatever my current
expression's eyes are, if the current expressions
eyes are closed, then I'm going to have them be open,
otherwise they will be closed. So I'm just toggling them. So eyes now which is of
type FacialExpression.Eyes, it's got the opposite or
whatever the old one had. Now, I'm gonna set my model to
equal a new facial expression. That new facial
expression's eyes are gonna be this new
eyes I just calculated. And the mouth is gonna be
whatever the old mouth was. I'm not doing anything to the
mouth, so I'm just gonna say expression.mouth, for
the old one. So here I'm changing
my model and that's gonna cause
this didSet to happen. It's gonna re-draw my view and the eyes are gonna be the
opposite of what they were. So that's the handler. That's gonna handle that. So now we have to add
a tap gesture recognizer to our face view. So this time let's just
say let tapRecognizer = UITapGestureRecognizer. And as always we just
specify target and selector. So the target this
time is not faceView, up here it was faceView. Now the target is myself. And the action is going to be, again I can say
faceView controller or I can indicate self dot,
we'll do that, toggle eyes. And we need to put
around this #selector. Cuz we need to turn it into
that particular selector. Then again, I don't actually
need that self in there, Looks a little better cuz it
defaults to my own methods. I can just do that. So we got the tap
recognizer created. Now we have to configure
the tap recognizer because we want it to
happen on single taps. So we could say for example
numberOfTapsRequired = 1, that happens to be the
default, so probably I won't need to actually say that,
but we'll do it anyway. And now we need to turn it
on and we do that by adding a gestureRecognizer
to the FaceView. If you don't do this step
of adding it to some view then that view is not going
to be recognizing this. So it's not gonna
be doing anything. Just an easy step
to kind of forget. All right, so let's see if that works. All right, so tap,
it's working. Every time I tap,
it opens and closes the eyes. And it's changing
it in the model. So it changes the model, that causes the update UI to
happen, this update UI does does this little thing
to update the eyes. When it calls eyes open
over here okay, eyesOpen. When it gets set,
it calls NeedsDisplay, which causes
the thing to redraw. Everyone got that
chain of events? Let's do one other one here. Which is to do a swipe. So I'm gonna have a swipe,
where swiping up makes our face happier and
swiping down makes it sadder. So this has to be handled
in the controller. Why? Because this is
affecting the model. Happiness is in the model. So I'm gonna have a func
called increaseHappiness. It's just going to set
the expression equal to the existing expression but
happier. I'll do a func
decreaseHappiness, i's gonna be the opposite, expression = the current
expression but sadder. And these vars here,
happier and sadder, those are in the
FacialExpression, right here. You can look at those later
to see how they work. But basically the're
just creating a new FacialExpression where
the mouth is happier or the mouth is sadder. And then the mouth knows
how to be happy or sad or it's using this raw value
mechanism in enum, hopefully you've figured that out in
your reading assignment. This is a great opportunity
to go figure it out if you didn't. Back in our controller here,
we now have these handlers. Notice these handlers
have no argument. Why do these handlers
not have an argument, like this ByReactingTo
guy down here. Because a swipe gesture
is discrete, so we never have to look at the
swipe gesture when it happens, we know it happened and
that's all we need to know. So these handlers do not ever
need to look at the state or anything like that, like a tap
gesture or a swipe gesture or a hand gesture. Let's add these guys to there,
so I'm gonna let
swipeUpRecognizer = UISwipeGestureRecognizer,
again, target and action. Again, the target is myself
because it affects the model. And I'm gonna have this be
the selector increaseHappiness because this is swipe up. Okay, now I have
to configure it. SwipeUpRecognizer's direction
is up, and now I'm gonna add it, faceView.addGestureRecognizer-
(swipeUpRecognizer). And since swipeDown is so
similar, I'm just going to copy and
paste here. Copy and paste. And we will change swipe
up to be swipe down. And of course, the direction
here now is down. And it's decreaseHappiness and
so I decrease. So I've added these two
recognizers, up and down swiping. And they're going
to increase and decrease our happiness
in the model. And when the model changes,
update UI is called. We're gonna look up mouth
curvature in that dictionary. We're gonnaset
the mouth curvature, that's gonna do the didSet,
we're in good shape. So here we have a pretty sad
guy, we'll open his eyes, then we'll close,
no leave them open. Now we're gonna make
him happier, and happier, and happier. Woo, he's really happy! And swiping down, no,
not so happy after all. So that's it for gestures. Gestures are super-simple and
super-powerful cuz all the logic going into detecting
was that a swipe, or was that a pan, or what was that,
was all done for you by iOS. Back to our slides. Time to talk about
building bigger apps, apps where we're gonna have
multiple MVCs involved. Not just our calculator MVC,
but multiple MVCs. Not just our face view
controller MVC, but multiple MVCs. So I showed you this picture
from the MVC discussion and you can see that the MVCs
relationship to each other is that one or more MVCs is part
of the view of another MVC. So let's see how that happens. We combine these MVCs using,
(I don't wanna say special, because you could
write your own), but built-in MVCs that
come with iOS. And these MVCs are built using other MVCs as
part of their view. So, yes you can
build your own. So, for example, here's the
three that we do most of our building with here or pretty
much all of our building, multiple MVCs. There's the TabBarController,
a SplitViewController and a NavigationController. So I'm gonna go into detail
here about how each of these controllers, are controllers
of MVCs whose view is other MVCs. So here's a TabBarController
for example. If you look at what
you see up there, there are two MVCs
on that screen, two. One MVC is
the TabBarController. It's what's drawing the little
icons at the bottom. You see those icons at
the bottom, those four icons? That MVC is doing that. The other MVC is the thing
surrounded in yellow, a dashboard MVC,
whatever that is, something in the health
app here or something. That is drawn by
a completely different MVC, the dashboard MVC. But notice how it's
drawing inside the view of the tab bar controller. So, you're already seeing here
how we're building an MVC out of other MVCs. The tab bar controller has
an MVC built into its view. Now, what's interesting is
this is very object oriented in that those buttons along
the bottom, both the icon and the text on and
if they had a badge, that would be there too. Those are actually not being
set in the TabBarController, the TabBarController
is grabbing them from the dashboard MVC. Grabs them from a var
in the dashboard MVC, it's a view UI controller
bar called TabBarItem. And TabBarItem has things like
Badges, and titles, and icons, things like that. And of course, we can have
more MVCs in the tab bar if I click on that second
icon right there, I get a different MVC filling
up this space at the top. So now,
we've seen three MVCs so far, dashboard, health data,
and the TabBarController. Right, now by the way, if you
get too many tabs, if you put six MVCs, for example,
into a tab bar controller, then you get a little more
"..." icon on the right, and you can click that and
the user can get at those. So a tab bar controller could
have more than five MVCs. But if you start
getting more than five, the UI gets a little clunky
because their user is having to go over and click that
more to get to the other one. I kinda recommend if
you have a tab bar, don't put more than
five MVCs in there. So the question is, when we
have the icons at the bottom and like, the icons and
the titles there. And I said that it's getting
that from the MVCs that are being shown. The question is,
is the embedded MVC, like the health data one,
or the dashboard one, is it getting the button and the
title there from the model? And the answer is,
that's up to that MVC. Some MVCs might well do that,
probably most are not because it's kinda more of a UI thing
what the icon and the text is. Probably not part of the
model, especially the icon. Unlikely to be in the model. We have more MVCs for a total
of five MVCs on the screen. The tab bar controller and these four MVCs that
are each of the tabs. So this is the simplest
way to combine MVCs. But do you see what I mean
when I say that these other MVCs are part of the view
of the tab bar controller. They're actually even their
views are embedded physically embedded inside the tab
bar controller's view. So that's the simplest one. Let's talk about
the next simplest one, which is split
view controller. So split view controllers
is two MVCs side by side. You get to see them both
on screen at the same time, at least in landscape you do. This one on the left
here is a calculator MVC. It doesn't look
much like yours but you can imagine that's
a calculator MVC. That MVC, we call the master MVC of
the split view controller. And then on the other side
there's a whole different MVC, completely and
utterly different. This one happens
to be a graph of what's in the calculator MVC. And this is gonna
look very similar to your homework assignment,
because that is assignment 3. To build a graphing
calculator, you're gonna be putting
in a split view and doing all the things I'm
talking about today, so pay attention. So that we call a detail. The reason we call
that a detail, is almost always
the right side, which is larger usually,
whatever's in it happens to be controlled by what's
on the left by the master. And that would be true here. Cuz the graph on the right
is showing whatever is in the calculator on the left. So if you change something in
the calculator on the left and hit its graphing button,
it changes the details. So the master is controlling
the detail in that way. Now, and this is what it
looks like when these things are side-by-side
in landscape mode. But what if you switch
over to portrait? Well, in portrait it just
shows you the detail, but you can swipe from the left
and get the master to come out on top of it and
then do whatever you want. It still updates
the utility and you can swipe it
out of the way. So that's split
view controller. Super simple. Again, hopefully you're seeing
that there are three MVCs The split view controller's
MVC, and these master and up here. detail MVCs. The master details
serve as the view of that split view controller. Notice that the split view
controller itself has no UI. It doesn't actually draw
anything in the view, it just uses the UI or the
view of these MVCs to draw. I guess, it draws the vertical
line between the two, that's probably drawn
by the split view. The most complicated one and the most commonly used one,
is a navigation controller. Another MVC that uses
other MVCs in its view. So, here we see two
MVCs on this screen, navigation controller
based MVC. And it's showing another MVC, which I'm calling
the all settings MVC. In this particular view
that you're looking at. See on the top there's
a gray bar that says settings in there? The gray bar, which is
actually transparent and showing the all
settings MVC behind it. Kinda, fuzzed out
a little bit. That gray bar is drawn by
the navigation controller. That and
a toolbar on the bottom, which I'll talk about in a
second, that's the only thing that the navigation controller
actually draws in its view. The rest of its view,
it fills up with another MVC. The view from another MVC, in this case the all
settings MVC. The contents are all
this "all settings MVC". Just like with the tab bar
controller where there was that tab bar item that
had like the icon and the title on it. Same thing with
the navigation controller. That word settings at the top, the navigation controller MVC
is getting that word by asking the MVC that it's currently
showing what's your title. It gets that from this
var navigation item. It's a UI view control thing, just like tab bar item has all
the tab bar things, navigation has all the navigation,
UI navigation things. So if you look at the API for
navigation item, it has things in there like
title, and I'm gonna show you another property that it's
gonna use in a second to get more information to draw. If I click in this
table view actually, if I click on general right
there, then a new MVC is gonna slide in on top of
that old settings one. Now, the old settings one is
completely non-visible but it's still there. This is like a stack of cards, where I've put
a new card on top. The other card is no
longer on screen, okay, you won't see it any more. But it still exists in memory,
in the navigation controller. So now we have three
MVCs involved in this Navigation Controller, the
Navigation Controllers' MVC, the all setting ones that is
now in the back of this one, and this one,
the General Settings MVC. Now, by the way, there, you can have toolbar buttons
along the bottom here. They look similar to
the tab bar ones, but they're
a little different. That is set with the toolbar
item, the var in the General Settings MVC, not
in the Navigation Controller. So as new MVCs slide in,
the toolbar buttons at the bottom would change,
because they're connected to whatever happens to be on
top of the deck of cards. Notice also that
there's a back button. You see that back button? It's called settings. That button was automatically
put there by navigation controller. And if you click that, it's gonna go back
to the other card. The all settings, so
that's all done for you. But if I click
somewhere in here, like on Accessibility there,
then it goes to another MVC. Now we've seen four MVCs so
far in this thing. And if I click on Larger Text
in here, I get yet another MVC.. Now we have five MVCs. So these MVCs just keep
stacking on top, and each of the new one that
comes on is its own world. It's completely on its own. And of course if we start
pressing the back button up there on the left, it goes
back to the previous one. Notice the title of
the back button is even set to be the title of
the previous MVC so you know what can happen
when you click it. And we can go all the way
back to the all settings at the very top. Now there's no back
button because there's no other cards. So that's what
navigation controller looks and works like. Okay, you guys have all seen
this UI, I'm sure, in apps. Super common. So let's talk a little bit of
how the navigation controller works behind the scenes. How how you make
this thing work. So when do you need
a navigation controller? Well, let's see you have
an MVC, right there, and you got another whole pile of
UI down here in the corner that you wanna add,
but just won't fit. You wanna be able to do
the accessibility stuff and the larger texts and all that
we saw on the previous slide. But it obviously, it's not gonna all fit
in one phone screen so you need more space. So what you do is you create a
new MVC that controls all that UI that is completely
independent of that. And it just knows
how to manage that. Now they might share a model,
because maybe they're all looking in
the settings database. That's fine. But they're not talking
to each other in any way. The only time they ever
talk to each other, is when it first
comes on screen, as you'll see in a moment. So I have two MVC's now, to control all the UI
that I wanna present. So let's use
a NavigationController to make these come on screen. And so
here's a NavigationController, it's just MVC. It doesn't really have a model
that's okay but it has a view. And inside that view there is
a special var that's pointing to its view called
RootViewController, that's just points of to the
controller of the MVC that's at the root it's
the base card. The all settings one we
saw on the previous page. So it just has this bar. You just set that
to something, and now you'll have an MVC. And as soon as you do that,
as soon as you set that, it's going to take
that MVC's view and put it in its view with
that little gray bar. And it's gonna ask it
navigation item for the title, and get the title. Perfect example. This is super simple. And then let's say you have
a button inside this UI, or like a line in the table view,
like get me more settings or accessibility or whatever. And you touch that. Now, when you touch that, it's
going to create one of these MVCs on the right,
it creates it at that moment. As soon as you touch it,
it creates a new one. Every single time you
go to a new card, it creates a new one. Not back, but every time you
go forward to a new card, it always creates a new one. That's something I'm
gonna say three or four times in this slide so as to make sure
you understand it. This activity of going to
a new one is called segueing. This particular segue's called
a show or a push segue. Which just means a segue in
a navigation control with the stack of cards. Now when you do that,
you get the back button for free, it just automatically
appears up there because that top bar like I say is drawn by the navigation
controller's part of its view. And when you click
that back button, of course what happens
is interesting here. Of course this slides back,
but what happened to that MVC? Gone. Deallocated from the heap. So when you go back, whatever card was on
top gets thrown away. So MVCs, as you're kind of
getting the picture here, they're pretty ephemeral. MVCs don't really stick
around for a long time. Now the root card on
a Navigation Controller, it's gonna stick
around pretty long, as long as that Navigation
Controller is on screen. But mostly MVCs kind
of come and go. And it's their models that
tend to be persistent. When you create a new MVC, you usually hook it up to
a database or a network or something, something
persistent. And so that's why it's
showing something new. Or it's an MVC that showed
something new every time. So that's how a navigation
controller works. I'm gonna talk a little bit
how we hook all of this up. Because of course we're gonna
do all this hooking up with control drag in Xcode, that's
how we hook everything up. But I wanna talk a little
bit about the code behind so you know. There's an important var that
is in these viewControllers, like tab bar controller, and navigation controller,
and split view controller. It's called viewControllers,
and it's an optional array
of UIViewControllers. And it means different
things depending on which one you're in. each tab, with the left most
tab's MVC's at 0 and then 1, 2, 3, 4 is just
the rest of the MVCs. For a split view 0 is always
the master, 1 is always the detail, and there's never
any other ones in this array. For a navigation control, 0 is
the base card, and 1, 2, 3, 4, etc., are the ones
stacked on top, in order. So that's how you can
kinda get at them. Now, we don't usually, this
is a bar, it can be set but we usually don't set this bar,
we don't set the things, we either use things that we
do in our UI, which I'll show you or even like in Navigation
Controller, there's push and pop methods to push MVCs on
and pop them off of the stack. Now, it's great to have this
var ViewControllers, but how do you get
the Split View Controller or get the Tab Bar Controller
if you're in one? Okay if I'm the all
settings MVC or I'm one of the health data
ones that we saw on the tab bar, how do I get the
navigation controller I'm in, or the split view I'm in or
the tab bar I'm in? Well all UIViewControllers
have these three vars, tab bar controller,
split view controller and navigation controller. And if you are in one of
those things this will return the one you are in. And if you're not in one,
it will return nil. So that's how you find out the Split View Controller
you're in. And you can use
the combination of For example, if you're
the Master in a Split View, these two things. you can find your
detail MVC by saying what's the split view
controller I'm in? Get its View Controllers,
look at View Controller sub 1 because that's
always the detail. So I just clicked and
went up and over and got it. You see? And I use the question mark,
the optional chaining there because I might not be in
a split view controller. And then it obviously
will return nil for my detail, which is fine cuz
it would be nil if I'm not in a split view controller
obviously there's no detail. Now, how do we wire
all this stuff up? Of course we're gonna
do it all graphically. Let's start by at looking
at the split view and how we do this. Now to create a new view
controller, whether it's one of these view controllers
of view controllers that we were talking about or just
a regular view controller. You go down to the object
pallet and you drag it out into your storyboard just like
a button, except you're just dragging to open space and
it's created there. Now when you drag a split
view controller out, it's actually gonna drag out kind
of empty, detail and master. You never want that. I don't know why it
really does that. Just delete those. You're obviously gonna
have your MVC's like your calculator and your graphing
view controller sitting in your storyboard and you're
gonna drag the split view controller and you're
gonna wire it up to those. So you're gonna delete any of
the extra junk that comes out. Then you just
Ctrl+drag like we do, our favorite gesture in
a face builder, is Ctrl+drag. We just Ctrl+drag from
the split view controller to the master and
to the detail, so I'll get up. So this is what
that looks like. Here I've got split view
controller on the left. I've got calculator view
control on the top and I've got a graph and
control view on the bottom. So I'm just gonna control, drag from the split
view controllers, the calculator controller. This little black window
is gonna come up. I'm gonna choose master view
controller from the list and that's gonna hook that
one up top to my master. Then, I'm gonna Ctrl+drag to
the bottom one, the black window comes up I'm gonna
choose Detail View Controller then it's gonna make
that connection. Okay, as simple as that. Now you've got the split
view with master that's calculator of your
controller and detail that is going to be
a graphic view controller. Now, one interesting
thing here though, is this split view does
not work on an iPhone. Well it works on iPhone plus,
iPhone six plus or seven plus, the kind of big
ones, but it does not work on a normal iPhone, it's just not
enough screen real estate to really do the split view
thing, so we can't do it. So, what we really want to
do is build a UI though that works on all the devices. So to do that,
what we do is we're gonna wrap the master and
the detail if we want. We're gonna wrap them in
a navigation controller. And then when
they're on iPhone, they won't use
the split view part, they'll just use
the navigation controller. It's really cool,
how it figures this out. The way to wrap a view
controller that's in there, you wanna wrap it in
a Navigation Controller, you just go to
the Editor menu, the same place we did
embed and stack view, and some of that. And you select the whole
scene of your MVC, and you go to the Editor Menu and you say embed in
navigation controller. So I've selected that top,
calculate and control and when I do that, it's going to put it inside
a navigation controller. Did you see that? Now, I've got the flip view,
it points to a navigation controller, so the master of
this flip view is actually that navigation controller,
and then the navigation controller root view
controller is the calculator. So, if you're on a device
that can do split view, like an iPad or a plus, you're going to get
the whole split view effect. But if you're on
just an iPhone, it's not going to
do the split view. It's just going to do
the navigation controller. So, when you look
at the detail, it's going to slide it in. Whereas if you're on
a iPad in landscape, you're gonna see Master and
Detail at the same time. Now, you can also do
the same thing- yeah, and so the UINavigationController
is the Master, as, as I talked about. So you can do the same
thing with the Detail. Now why would you ever
do that with the Detail? Because yo've already done
it with the master and so you gotta click in the
master to get to the detail, so yo're kinda already gonna
be in a navigation controller, and tha's true. But wha's kinda nice
about putting the detail in a navigation controller is
you get that little title bar, you know the little
gray title bar? So now, your master and your detail both have
a title bar on the top and they match up nicely. So if you wanna title
on your detail, that's a good thing to do. The only thing to be
careful there though is now your detail in not
a graph view controller. Your detail is now
a navigation controller. So if you wanna get the graph
view controller and like talk to it,
tell it what to graph or something like that,
you need to get the root view controller of
the navigation controller. So it's just subtle
difference but if you wrap your detail in
a navigation controller, the detail has come become
a navigation controller. It's easy to get to
the graph view controller, cuz the root view controller
of the navigation controller. That's how you wire
up the split views so that it works cross platform,
all devices. The last thing we're gonna
talk about is segueing. So we know how to wire up
the split view controller, and we know how to do
a navigation control, we just embed it in
the navigation controller. I didn't talk about the tab
bar controller by the way, but that's easy. You just drag a tab bar
controller out and CTRL + drag to all of the MVC's that
you want it to show, and you can actually even
drag the little icons around to put them in order. You can even set the icons in
text in the interface builder with an inspector. So, tab bars, so simple I
don't even want to waste time showing it, but so we know how
to kind of hook up and control drags and wire up, or embed to
do the navigation controllers. How do we go from one to the
other, like in the navigation controller, how do we go from
all settings to settings and from settings to
accessibility, and accessibility
to larger text? How do we set up that
transition or how do we make it so that we press a button
in our calculator split view and it causes that graph to
be replaced with a new graph? How do we do that? We do that with what
are called segues. I've alluded to this earlier. These are the kinds
of segues that exist. There's a show segue. That means, if you're in
a Navigation Controller, slide a card on. There's Show Detail, which if you're
a Navigation Controller, still means slide a card on. But if you're inside
a Split View, it means put this new MVC
where the detail goes. That's why it's
called Show Detail. So Show Detail behaves
differently depending on whether you're in
the Split View or whether you're in
a Navigation Controller. Modal segue,
it's kind of interesting one. It takes over the entire
screen and lets the MVC that you're segueing
into own the whole screen. Now, these can be somewhat
disturbing to the users cuz, I can't get back
to where I was, cuz there's no back button or
anything. That whatever MVC comes up and takes over the screen has to
provide some UI to get out of there like cancel or
done, or something. So we try to stay away
from Modal if we can. So Popover is sort of
the same as Modal, but instead of taking over the
whole screen, it just puts up a little pop-up window,
a little popover window, you've seen that,
on iPad especially. It's still Modal, because
if you click anywhere else, the popover goes away. So it's essentially modal. It's a little nicer
than modal, though, because you can really see the
background kinda grayed out. And if you don't want
what's in the popover, just click somewhere else and
it goes away. So yeah, popover's
a little nicer than modal. But it only works on
fairly large screens, unless you have a really
small pop over window. Now, I'm going
to emphasize for the second time what I said
before segues always create a new instance of the MVC. All of the segues, all of
them, modal, popover, show, slide in the min, show detail, even show
detail in the split view. If you have a calculator and
you say graph my function and you segue to a graph
view controller, it throws away the one
that was there and puts a new one there,
always creating new MVCs. So think of these MVCs as
ephemeral, they come alive, they do their job and
they go away. They don't live you know,
long lives. Now the master and
a split view. The root view of
a navigational, they tend to live fairly long. But even those, if that split
view, if that navigational were to go off screen,
they go away as well. So how do we make
these segues happen? Of course,
we use control drag. We love to use control drag,
so here for example is
the calculator example. I've got a split view. And in my calculator
there in the upper right I have a button it's
very hard to see but it's a little
picture of a graph. So that's the button
that when I press it, it's gonna take whatever's in
my calculator and graph it. So if I've typed in for
example m cosine, it's gonna draw a cosine wave,
that's what that button does. So I just go into interface
builder here, control drag from that button down to
the detail right there. When I let go, it's going to put up just
a little black window, which I'll show you
a little detail of. It looks like that. And I'm gonna pick
the segue I want. Well, since this is
a Split view, if I can, I'm going to show detail. Put it on the right
side of the split view. Now, if I were in this
same storyboard, but I was on an iPhone non-plus, then even though I'm
picking show detail here, it's still going to do the
navigation controller show, because there is no split view
side, when I'm on the iPhone. So I pick the segue
type I want, and it creates the segue. And it appears in
your storyboard, it can even be clicked on and
inspected. And in fact you're gonna
always want to click on that thing and inspect it. Cuz you don't wanna set
this thing right here which is the identifier. Every segue has an identifier,
it's just the string and that identifier is what's
you're gonna use in your code to talk about this segue. And I'll talk
about what you've been talk about regarding
segues, any moment here. So always set that. And you wanna set it, the identifier to be something
that kind of says what is segue does. So in this case it
shows a graph, so I call this show graph. That's how you set
up your segue. And that's true for
navigation controller as well. If you have a navigation
controller and you have a button that causes
another card to slide on you just control drag from that
button to the other MVC. And then you use a show segue,
and it'll just work. Has to be all inside, embedded in navigation
controller, of course. So what can you do
with these identifiers? It is actually possible to
cause a segue to happen from code, with performSegue
withIdentifier, but I'm not gonna talk about that,
cuz we're never gonna do that. We always gonna have buttons, well I'm not gonna say
we're never gonna do that, but we won't be doing that
in your assignment three, that's for sure. It's pretty rare. But you can do it
with performSegue. But the more important use of
the identifier is not causing segues to happen cuz
segues kinda happen automatically when
the buttons are pressed. It's to prepare for a segue. This is the most important
method in all of multiple MVC programming
is this preparing for segue method which I'm gonna show
you in a moment here, okay. And remember, we're all
segueing to a new MVC freshly created, so
it's always raw. So we have to prepare it to
do what it's going to do. Here is the method. It's called prepare for
segue, and it also has a sender
argument right there. The segue that's passed
along that first argument is just a little container
that contains interesting information about the segue. For example,
the identifier, Show Graph. And very importantly, the
controller that you're seguing too, because the whole
point is here, you're suppose to be preparing
this MVC that's going to come on screen. To do its job so you get that thing from
this little segue object. The sender is kinda
like buttons, the button sender when
we had the action there. It's just what object
is instigating this. So in the calculator example
you're gonna have a button clicking causing this segue,
it's gonna be that button. Okay, the button that's
causing the segue. But it's Any because anything,
when we saw the example of the all settings, we were
clicking on table view cells there not buttons but cells in
what's called a table view, which we're gonna talk
about week after next. And so
that could be the sender. Or you might have done perform
segue on the previous slide, that actually let you
set the sender so you've tell it anything you
want, it could be nil also. We don't use it that much. The first thing I'm do is going to be
get the identifier. Note that it can be nil so I'm gonna say if let to get it
out of that segue right there. And I need that identifier cuz
I need to know which segue I'm doing because I might
have three buttons on my calculator that segued
to three different things. Now you're not gonna have in
assignment three but you could have multiple buttons
segueing to different things. So you need to know which
segue you're talking about. So you switch on it and
then you check the case. And in the case of Show Graph, which is the one we're
gonna do right here, we're going to prepare that
graphing MVC to do its job. Now, in order to talk
to that graphing MVC, we have to get it as
a graph controller. Now the type of that
destination view controller that's in the segue object
is just UIViewController, it doesn't know that it's
a graph view controller or something like that it's
just UIViewController. So we have to use as, remember the "as?" I told
you you could cast anything. Not just any object, you can
cast a UIViewController to try and down cast it to
a specific view controller. So here I'm going to try and
cast the destination view controller to be
a graph controller. I'm gonna assume the graph
controller is the type of the controller
of my graph MVC. And if I'm able to do that,
which I should be, because I know this is
the show graph segue, so I better be, if, if I'm not then probably I
wanna raise an exception here. Because I'm really expecting
to be able to do it. Maybe I use as exclamation
point right there instead of as question mark as it'd be
bad if I was doing this and it wasn't actually
graphcontroller. But anyway,
I get the graphcontroller so now VC, this variable VC is
of type graphcontroller, so I can start setting
vars in it, calling methods, whatever I
need to do to prepare that graph controller
to do its job. Now in the Calculator case, what I'm doing is I'm
going to somehow have to set the model of
the graph you control or something to show
the graph I want. Based on what 's
in my calculator. So, I'm preparing it
to do what it does. I'm pretty much never going
to talk to this thing again. So I'm gonna give it all the
information it needs to do to go to a job. This is kind of one
time set up and that's the way MVCs need to
work through, object oriented. They get set up once and then
they just live on their own, they do their own job. They don't talk back to the
person who brought them up or to anyone else. They live on their own. So you can do anything you
want to here, to try to do it. One thing to warn
you here though and half of you will
trip over this. When you're doing
this preparation of that graph controller, that graph controller's
outlets have not been set. I told you that IOS hooks up
those outlets to really really quickly, but this is one case
where it doesn't quite hook them up fast enough. When you're doing
vc.property1 = whatever, vc.callMethodToSetItUp, at
that time, none of vc's outlets are set. So you can't do
anything in the UI. So if you're collecting
information for what to, to do in the UI, you're gonna have to store it
in some vars or something. And then later,
either in the did set of those outlet centers, or
I'm gonna teach you about view controller life
cycle next week. There's other methods
that happen later, that you can be sure
that the outlets are set. So this is in red, any time I
put something with red inside, you really wanna
pay attention. So the outlets are not
set in graph control or when you're preparing,
the process of preparing. You can also prevent
a segue from happening, let's say if the calculator
and you try to hit graph and you're on the middle
of a binary operation, pending binary operation. Well you can't graph that. There's no way to graph
a pending binary operation. Three times, what's
the graph of three times? It's nothing. So, you wanna prevent it. So, in your assignment three
you're gonna wanna use this method and be able to control
a shared perform segue, you're gonna return false. When you're in the middle of
a pending operation, okay? And so that's it for today. There's no Friday
section this week. Next Friday section is going
to be on performance analysis cuz there's going to be
an extra credit item in assignment three
that does that. You'll see that
when it goes out. Next week I'm gonna do a demo about all this
multiple MVC stuff. I'm gonna start talking about
the View Controller Lifecycle. And then the super important
topic, Protocols and Delegation. UI things like scroll
views and table views. We'll start that next week cuz
all you're learning up till now is kinda the basics. Swift and views and
multiple MVCs. One you got that basics in
now you can start doing these more powerful objects. Now, assignment three is
already on the class forums. You can start it right now. It's got really two
major points to it. One is a custom UI view
that does graphing. A truly a custom MVC
that does graphing. And that thing is completely
independent of your calculator. In fact I would recommend
you create a new app to create that MVC. Because if you don't wanna be
polluted by you calculator, it's just a generic
graphing MVC. Once you've got that, you're gonna combine it
with your calculator like I'm talking about here, where you're going to
click a button, and it's going to graph what's
in your calculator. So you can get started on that
today because you already know everything you need
to know about UI view and doing a custom draw method. So you could start
drawing your graph and building that custom
graphing view. You could even build
the custom MVC because you're building it as
a single MVC to start. But you're gonna wanna
wait til Monday to try and hook it up to your
calculator because you're gonna wanna see me do
the demo of a split view and all that stuff and
see that happening. It'll help you when you
start hooking your two MVC's the graphing one and
the calculator one together. And because you
need next Monday this assignment's not gonna
be due until the next Monday. Okay, so you got a nice 10 or
11 days to get it done. But I strongly recommend you
use the next few days to go ahead and get started
on the view part of it. Because that's more than
half of the assignment and you can make a big
head start into it. The last thing is the last
reading assignment also went out today. The due date is pretty
much next Wednesday. But that is going
to make it so you've read the entire
Swift document. So by the end of
this assignment, you should feel like you know
everything that's in there. Now of course, I don't
expect you to know every single detail about
every single thing. But I expect you if you like want to do
a nested function I expect you to be able to know where to
go look to find that out. Or you wanna do
optional chaining and you can't quite remember you
know where to go look it up. That's a reference document. So you wanna know
what's in there. Even if you don't
memorize at all. That's it. See you next time. >> For more, please visit
us at stanford.edu.