Stanford - Developing iOS 10 Apps with Swift - 5. Gestures and Multiple MVCs

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> 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.
Info
Channel: CS193P
Views: 2,109
Rating: undefined out of 5
Keywords: iOS, Swift, Stanford
Id: UWN2m6BC8e8
Channel Id: undefined
Length: 81min 18sec (4878 seconds)
Published: Tue Mar 14 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.