Developing iOS 11 Apps with Swift - 5. Drawing

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC] Stanford University. >> All right, well, welcome to Lecture 5 then of CS193P this Fall of 2017. So I'm gonna take just a few minutes of the beginning here to go over just a couple of more little Swift things. These are actually in your reading assignment for this week. So I'm really just kinda emphasizing these things, then we're gonna dive into our main topic today which is views, drawings, how you do custom drawing in iOS. And I'm gonna start with a huge demo, the only thing I'm gonna probably be able to do today is the model of our MVC. But that's gonna allow me to show you an example of enum, which we didn't get to work into concentration. So you only theoretically know enum, and we'll be actually do enum. All right, so let's do these little minor things. One of them is error handling in Swift. Now, a lot of times you'll have an error and it will be something that kind of, could be expected or anticipated. You know, you do something over the network and there might be a network time out, that's kind of a normal error. If you have that kind of error, you likely would have an argument to the function that goes and does that network call. Maybe it's even a closure that calls you back and said, I found this somewhat expected error. But sometimes you have errors that you don't expect or that are kind of rare, and really disrupt the usage of a method or whatever. And in that kind of error, instead of having to have one of your arguments be an error handler or returning an error, or something like that. You can do something that's called throwing an error. Now, in other languages, you might think of this as like raising exceptions or something like that. It's a little simpler and more elegant when you design in Swift. It's as simple as this, a method that could get an error, okay, a significant error that would disrupt what it's doing, can decide that it throws errors. And you see these methods very clearly because when they're declared, they'll have the word throws at the end. So this function save throws. Okay, if it gets an error, it throws this error. Now, when it throws an error at you, you need to catch it. Okay, and you catch this error and then you can look at it and decide what to do. So how do you catch an error that has thrown? Well, you do that by putting this do catch construct around your call to the method that might throw. And you say, try, in front of the call. So you're going to try this method that it might throw. But since you've put it inside this do, catch thing, that thing you see up there, you're going to catch that error. Okay, so it's as simple as that. So, any method that throws has to be called with try. Okay, you have to let Swift know I understand that this can throw, and so I'm trying it. But it doesn't necessarily have to be kept, caught, and I'll show you in a second how to do it without catching. If you do wanna catch, then you do this do catch, and in the catch you notice there's the green, let error. Okay, that little error thing, it's just a local variable. That's the thing that gets thrown at you, okay, when it throws an error. And it's going to be something that implements the protocol error, but there's actually nothing in that protocol. It just identifies, it's just typing this thing as error. Now, in iOS when things, errors gets thrown at you, there almost always going to be NSErrors. So an NSError is just a class, it implements the Error protocol which is to say that, that nothing in particular. But NSError has a lot of nice methods and vars, like the error code, even a localized description of the error that you could present to a user, for example. So the way this goes is if you had a function save that throws, you're gonna go looking in its documentation and see what kind of NSError stuff it can throw. And it'll throw certain errors like save, the save maybe it's for saving into a database and the disk is full, so the database couldn't save so maybe there's an error code for disk full or whatever. So this is what you do if you want to catch a thrown error and kind of handle it, look at it, see what it is, decide what to do. But you can just say, try!, and what that means is, try this and if it throws crash my app. Now, you would only do this if you were 100% certain that, that thing could not throw in the circumstance that you're doing, which is extremely rare, so we rarely do try!. But a better one if you don't care about the error is try?. So try?, means please try this and if it throws, don't crash, but just ignore the fact that it threw. So this is kind of like try and ignore. Now the interesting thing is, you might be calling a function that returns a value. An int that can throw. So here I've got my error prone function that returns an int, okay, and so what if I wanna try it with try?. Well, if it succeeds, I need that int back. But if it fails, I can't get the int back. So what happens when you do try?, it changes the return value essentially, of the function to be an optional version of that value. So if you have an error prone function returns an int and you try? it, and you let it equal something, you know, let x equal that. This x now becomes an optional int, because if it throws, it's gonna give you nil. If it doesn't throw, you'll get the normal x value that error prone function that returns an int, returns. Okay? So that's error handling. There's not a lot of methods that throw in iOS there, in there occasionally. You know, you might have a typical app, maybe you have three or four of them, somewhere. They're fairly rare but you got to know how to handle them. So these are the ways to handle them. All right, I wanna circle back now to Any. This type Any that we saw with NSAttributedString dictionary. There's another one called any object which is exact the same as Any, it's just for classes only, and the object is the any of classes only. And I told you that Any means, you don't know what type it is, it could be any type. And we also know that that's not very Swifty, and cuz Swift is strongly tight. So Any and AnyObject are in there for compatible with Objective-C. Objective-C had a very important type called ID which was essentially any and it was built in to all of the APIs. So when Swift came along and tried to be strongly typed, it had to have this one concession. So we don't use as I've said, any in our own code, it's for backwards compatibility. So variables of type any could hold any type. Structs, enums, classes, anything. And the problem though is, you can't send any messages to something of type Any, because you don't, in your code, know what it is. Okay? Now, Swift, under the covers, might know what it is, but since you've typed it Any, it's assuming you don't want to send any messages to it that you don't know what it is. So, how do we deal with that case in Swift where we got the strongly type language, we can't send a message to Any. Well, we got to convert it. Now, let's talk about where you're gonna see it, you already saw it in NSAttributedString case, right? Well, we have that dictionary. Another place you will see it is arguments to methods. So here is a method called prepare for segue. It's a UIView controller method, right? You all know what a UIView controller is? We made one for concentration. And I talked about when we do MVCs and we have multiple MVCs, and they own a whole screen. Well, we sometimes transition from one MVC together, to another. And this prepare for segue, a segue is a transition from one MVC to the other, this gets called in a view control when transition happens. Well, one of the arguments to it there at the end is sender of type Any?. Okay, an optional Any. And that's basically what button cause this MVC transfer to a new MVC so. So it could well be a button that, that sender is but it can also happen if you click on a row in a table view. So that's not a button, that's a table view. Cell, okay, or it could happen from something else. It could even happen by code, in which case this is a nil. So that's why you need Any right there, because you're not sure whether it was a button or a table you cell or something else that caused this thing to happen. So you'll see it as arguments, a case very rarely to a function like this where you can kinda pass anything in there,. But how are we gonna use this Any stuff? Okay, let's say a button was passed on your table, how do I know what it is and talk to it, and all that when it's this Any thing. Okay, before I show you how to do it, of course don't use Any in this course except for to do backwards compatible call APIs. You don't have your own data structures would not be use Any. Some people try to use Any when they basically don't know how to design a data structure, and they're like, I'll just make this an Any, an array of Any and I'll just start throwing random things in there. No, we don't do that, okay, that's not, Swift. All right, so now how do I use Any. I have to convert it to a type that I do know, since I can't send any messages or vars, I have to convert it. And we convert it, with a special keyword and Swift called as, as question mark. And what as does, is it just tries to convert that any to the type you specify and if it can't, it returns a nil. That's why it's as question mark. And it's as simple as that. So here's an example, it's best learn by example here. Let's say, I have a var called unknown, which of type Any, so I don't know what's Any. It could be anything in there, and I think that that thing in unknown might be of type, MyType. I'm not sure but I think it might be might type. So I'm gonna say if I can let foo, which is a new variable, equal unknown as MyType. Then inside there, I can send messages to foo that MyType understands, right? So I've just converted unknown into a new variable of type MyType, if possible by using as. And you can see this is nice, it reads like English. If I can let foo equal unknown as MyType, then I'm going to use it as MyType. So that's it, it's very simple, that's how we access Any. And if it wasn't, if unknown was of some other type and couldn't be interpreted as MyType, then this the curly braces, that stuff doesn't happen. You could even say else, and something else, and something else would happen, that would be fine too. Now this casting of As is not just for Any, we can also cast other things. Why, why else would we ever want to cast? Well one classic one to cast, is to cast from the variable type to one of its subclasses. So let's look an example of that. So here I have a variable vc, very importantly it's of type UIViewController. In other words the iOS class, UIViewController that's the type of vc. Now I'm setting it equal to creating a ConcentrationViewController, which is a subclass, let's say, of UIViewController. Now the type vc, even though I assign to it a ConcentrationViewController, that type is still UIViewController not ConcentrationViewController. That's not its type, its type is UIViewController. But this is a legal statement, because of course a ConcentrationViewController inherits from UIViewController, and that is a UIViewController. Now if I wanted to send flipCard, lets say that was a method in ConcentrationViewController, to vc I could not do that. It would not even compile, why? Because vc is subtype UIViewController, and the class UIViewController doesn't have flipCard. ConcentrationViewController has it, but not UIViewController. So people get confused about this, it's kind of like back when we're talking about the protocols and I had things to move. And it was of type movable, and I had a car in there and I want it to send a change oil and people were like, why not? Well, because the variable was typed to be a movable, and movables didn't know how to change oil, cars do. This is the same kind of thing here, exactly the same thing just with inheritance instead of protocols. So what if I did wanna send flipCard to vc, how would I do it or I can cast with any in exact same way? I can say, if I let some new variable called cvc, ConcentrationViewController equal the vc as a ConentrationViewController. Now cvc is a ConcentrationViewController, I can send it flipCard. So same way I did with Any, I'm checking to see if it's possible for this var to be converted, in this case downcasted. This is called downcasted, to a subclass of that thing, so I can interact with it as that subclass. Same thing happen here with protocols, we could do that same thing with the car in the movable. We could had a movable var and try to cast it as a car. And if you were successful, then we can send a change oil. But if it was a shape and that failed, we would return nil and we would not be able to do it. So you're gonna see us doing this casting not as much with Any, some with Any, but a lot of time just down casting. Because we have some function that takes some super class and we pass a down cast subclass into it. And we're gonna down cast it to look at the subclass in case it is that subclass, then we can send it messages. All right, so that's it for Any and Castings object. The last last slide that I have here before we dive in to views, is just four other interesting classes that you should know about in foundation. One is NSObject. NSObject is the root class of all classes from Objective-C. All the things like UIViewController, all the UI kit classes, all those things that were developed in the Objective-C world, and are compatible still with Objective-C. Their root class, the thing they inherit from eventually at the top, is NSObject. In Swift, no requirement to subclass from NSObject, although there's a few tiny esoteric APIs that ask you to pass a class in, that has to implement NSObject. And I'll explain them to you when we get to it, but normally your Swift class, like remember concentration that class, we originally made it. It didn't inherit from NSObject. It didn't inherit from anything. When it was a class, we converted it to struct later. And that's perfectly legal in Swift, that was not true in Objective-C. In Objective-C pretty much all classes had to inherit from NSObject, because some of the runtime was built into this class rather than being in the actual runtime like it is in Swift. Okay, second interesting class is NSNumber, in Objective-C when you pass numbers around you actually pass them with this class. NSNumber, it's a class so it's a reference type. You didn't have double and int as classes. You had C like doubles and ints in Objective-C, but you didn't have that. So if you wanted to pass something in an object oriented way, you had to pass this NSNumnber. Now it can represent any kind of number, floating point number, integer, anything. It's a kind of a generic number holder, even a boolean it can hold in there. Now it's really awesome is all the API throughout iOS that takes an NSNumber and there's a lot of it, automatically bridged to the Swift types. So if you have an Objective-C based API somewhere in iOS that takes a number as an argument, you can pass a double in there. Or an int, or whatever it's expecting, whatever it's supposed to be. You don't even tell, you won't even know, you won't even see NSNumber because the documentation is already being converted as well to show you. And similar, when things come out, if something comes out of the API as an NSNumber, you can work on it as a double or an int, or whatever is appropriate and it work just fine. But I just mentioned in case you see it and wonder, what is that? Is this old Objective-C generic number holder? It's easy to see create one from a double or int by just using an initializer, right? NSNumbers sub 35.5, that's gonna give you an NSNumber with the double 35.5 in it. And you can get the values out with vars on the number like intValue, doubleValue, boolValue, they will give you. The return in a Swift type double, int, bool or whatever. Okay, Date. Some of you already know about Date because you did the extra credit maybe, in assignments 1 or 2. Date is just a way to represent any date and time. You know internally it's represented, I think, like Number of seconds since 1970 or something like that. It's great for doing that, it's great for even small amounts of time because the date includes the time down to small you know, sub millisecond times. But I just want you to note that with date there's other classes that go along with it like Calendar, DateFormatter, DateComponents. DateComponents will give you things like the month and things like that. And why are there all those other classes? Well because date, if you're gonna put it in your UI, you have to be very careful. Around the world, dates are represented very, very differently, and it's not just that the name of a month is different in a different language. It's that other locale, we call them around the world, use completely different kinds of dating systems. Maybe even a completely different calendar than we use. So you really have to be careful if you're gonna put date in your UI to understand all of these other classes. And there is plenty of documentation how to do it. But, and it is extra work. Now if you're just using date internally to like keep track of how long your Concentration move, or set game move is taking, obviously you don't need any of that other stuff. And finally there's data. Data is just a bag of bits, we use it to transfer data around a lot inside iOS API. For example if we got an image from the Internet, from some URL, it would come in as a big bag of bits. And we have classes like UI image, which I'm gonna talk about today. That can look into the bag a bit and say, that's jpeg data and make an image out of it. Or look into a bag of bits, you know we have formatters that look and say that's json data. Good, who knows what JSON is, raise your hand if you've heard of JSON, okay, great. So that's JSON data, we interpret it as JSON data. So that's what this data struct is, it's just a bag of bits, and there's lots of methods to convert it to and from other kinds of things. That's it, so that's all I wanted to mention is those quick things. Now we can dive into our main topic today which is a really cool one, which is Views. All right, so I don't want you to be confused about the word view. I'm using it in two contexts. One is MVC. One of the letters there is V for view. That means that bunch of generic minions of your controller. That View, capital V View, contains a lot of views. These views that I'm talking about, and what by these Views, I mean a subclass of the iOS class UI view. So that's what we're talking about today is these Views, these things in an MVC's view. Sorry for the terminology being the same there but same word, somewhat different meanings there. So what is a view? What is a sub class of UIView? It's essentially just a rectangle on screen that defines a coordinate, a coordinate system. And this is a coordinate system you're gonna use to draw, and it's also a coordinate system for getting multi-touch events, right, with the fingers on the screen. It's hierarchical, which means these rectangles live inside other rectangles, live inside other rectangles, right? So the views inside views inside views. And you saw this with Concentration. We had that top-level view which was black, and then inside that we had stack view. Remember that? So that was another view. Inside that stack view were three more stack views. Inside those three sections were four card buttons. So as the UI buttons, right. Plus you had other views like your new game button, score, flip count, those are all views. And they are all kind of in each, you know, in a hierarchical representation. So the way that if you look at a particular view and you wanna see who its superview is, in other words the view it's in. You just ask the view for the var superview. Okay. Now, it's an optional UIView because it might not currently be on screen, right? So, it might not be in a superview at this time. You could certainly add it. And then the other way around if you have a view and you wanna say, what are all the subviews in it? Like you wanna ask our blackview where, show me all your subviews like this top level stackview and the flip count, label and the score, and the new game button. You get that with another view var called subviews, which is, as you can see, an array of UIView. Okay, so it's very simple to manage this. There is also a UI window which is like way up at the top, but we never pay any attention to that in iOS. It's not like the Mac where you have lots of windows on a big desktop. The UIWindow, the only time you probably ever care about that maybe if you had an app that like projected part of itself out onto external screen or something. But forget that. So in this class we won't even mention UIWindow. It's probably subclass UIView anyway, but we don't really talk about it. Now this hierarchy of views inside views, you build in Xcode in interface builder. Right, you saw how we built in Concentration, we drag things out, we clicked the embed stack view button, that kind of staff. So that's how we build this hierarchy, put views inside views 99% of the time. But it can be done in code as well. And in your assignment 3, I am gonna ask you to do both. Build view hierarchy in an interface builder like you did in concentration. But then also do some in code, as well. And the way you do that is by just saying addSubview to a view, and it will add that view as one of the subviews. The only tricky thing here is when you wanna pull it out for If you wanna pull a view out of the view hierarchy, some reason. you send that message to the view itself. Okay, you send it to the view, you want to remove and you say remove yourself from superview, with this remove from superview. So it's a little different there. You send add to the enclosing view but you send remove to the actual view itself. Now where does this view hierarchy starts. What's the top containing view? Well of course that's like the black view in concentration, right, that very top view. And there is a very important var, in UIView controller, called view. And that view points to that black one at the top. And it's automatically wired up for you in interface builder, so you can always start there and kinda of work your way down through the hierarchy, looking at the sub-views and going on down. So var view which is a UIView, in UIView controller, very important var to know if you want to access your view hierarchy directly. Now of course, you can also access your view hierarchy at any point with an outlet. Right, if you create an outlet like to a stack view, now you can look right at the stack view, and then start looking at its subviews if you want, you don't have to start at the top and start looking down. You can use an outlet, just go to any view you want at any time. All right, let's talk a little bit about initializing a view, right? Getting initialized. Normally, as usual, we try to avoid doing init as much as we can, with all those other ways we know. If you have to do a view initializer cuz you just, a var that you just no way you can initialize it using all the other ways we know. Then you have to understand that view has two initializers, init with frame and init with coder. Init with frame is the initializer you usd to create a view in code. Init with coder is the initializer that's used when you build your view in an interface builder and it gets free stride and then your app runs. When it runs, it gets initialized with init with coder. The coder, NSCoder is a protocol that handles this mechanism of freeze drying with interface builder and then re, resurrecting it when your app runs. So you have to implement of both of these. And the reason you have to implement both of these is cuz init with frame is a designated initializer, okay. And so if you ever created a view from code, you need, if you want your code to execute, it's gonna have to do it. And then init with coder is a required initializer because it's part of a protocol that UIView implements, this NSCoder protocol you see. Remember, if you employ a protocol and it has an init in it, it's required, okay. So you'd have to implement them both. Now you can have some other function like set up or whatever and call that setup from them both. Although, sometimes it's not that simple because if you're using this mechanism to initialize your own vars, you're supposed to initialize your own vars before you call super.init. Okay, so you are gonna have to initialize those vars inside these inits. Maybe you might have slightly duplicated code there, doing that. But hopefully, you can avoid this init in UIView entirely. Okay, another alternative to initializing the UIView is awakeFromNib(). Now awakeFromNib() is actually a function, that is sent to every single object that comes out of an interbase face builder file. Every UI button, every UIView controller, everything that comes out of there, when it gets unfreezed, it gets sent awakeFromNib(). Now the only thing about putting initialization there is, it will only work for views that come out of an interface builder file. If you create a view with init when framed from code, if you created one, this won't get called. Okay, this only happens when you get unfreeze write interface. NIB is kind of really old name for interface builder files, the IB and NIB meant interface builder. Okay, so that's it for initialization of views. All right, now let's talk about how we draw. We have this UIView. Why do we want it? Well, we wanna draw. How do we draw? Before I can tell you how to draw, we've got to talk about some types, four very important types. Okay, they all start with CG, like this one, CGFloat. CG stands for Core Graphics. Core Graphics is the underlying drawing system for normal 2D drawing in iOS. There are other drawing systems for doing 3D and other things. Not gonna talk about those cuz we only have so much to talk about. We're just gonna talk about the base 2D drawing system, and it's called core graphics. So core graphics has these four types, they're just critical to be in the say anything about drawing, one is CGFloat. So all drawing is in an accordance system that's floating point numbers. So you're drawing in a floating point coordinate system, not integers, they're floating points. When you say where you wanna draw, your every point you're just dealing with floating point numbers. And for those floating point numbers have to be CGFloat. They cannot be doubles or regular float, they have to be CGFloats. Luckily, there's an initializer for CGFloat. We'll let you create a CGFloat from a double, but CGFloat is the fundamental, kind of coordinate value, a floating point co-ordinate value. All drawing, all your code that's drawing is gonna have CGFloats. Now, of course, there is CGPoint, which is just a struct with two things in it, x and y, which are both CGFloats. And there's CGSize, which is a struct that has two things in it, two CGFloats, width and height. Okay, so you got float, CGFloat the base thing, then you got point and size. And of course, you can combine point and size into very important CGRect. Now CGRect is just finds a rectangle obviously, including your views entire bounds, that rectangle or any rectangle you wanna draw inside your quadrant system you're gonna use this. And it has a number of initializers including initializing by origin and size. Also intializer that takes xy width, height, etc. And it also has a whole slew of other vars and functions for manipulating rects, like vars like minX, which gives you the minimum x value of your rectangle. Or, intersects that takes another rect and returns a bool whether the two rectangles intersect. There's also intersect which returns a new rect which is the intersection of two overlapping rectangles. It also has contains CGPoint, which will tell you whether there's a point, that is point is inside CGRect. So there's tons, this probably two or three dozens methods so definitely check out the documentation CGRect, because it'll make your code a lot cleaner. And actually, you're gonna see in the demo that I do on Wednesday. I actually even extend CGRect to add five or six more a little convenience functions because CGRect turned out to be so important in all the code that you write. When you're doing drawing. So now you know those four types, let's talk about the coordinate system we're gonna be drawing in here. The most important thing to know besides the fact that it's floating point precision to draw is that the origin is in the upper left, not lower Left. The lower left would be Cartesian coordinates, you know, like you have in Math class. Lower left alk, also happens to be the coordinate system on the Mac, okay. But in iOS, it's in the upper left, and that means that increasing values of y go down towards the bottom of the screen. So for example, I got this point over here on the side there. See it up there, 500, 35. It's 500 over and 35 down, right, way over and way down. So that's x of 500, and it actually really would be 500.0 and 35.0 cuz they're floating point values, right? Okay, the unit in this coordinate system are called points. Points are not the same as pixels. Pixels are the little dots that the screen is made of. Some screens have a lot of little pixels, very, very high resolution, what we call Retina Displays. You've probably heard that phrase. Lots of pixels per point. Why is that good? Well, because remember you can draw on floating point boundaries, so you can draw at 27.3, 27.6, 28, right? You could be drawing in between points. And you get these very smooth curves or whatever on a Retina Display. On a lower res display, maybe there's only one pixel per every point, and so it's kind of more jaggy when you draw. But you don't draw on pixel boundaries, you draw on point boundaries. That way, even if you had a lot of pixels per point, it looks the same size basically as on a lower resolution device. Okay, it's not as smooth as on a high resolution device but it's the same size cuz it's zoom point. You can find out how many pixels per point your device has by using UIView content scale.factor who turns a float, it's currently gonna either be one, two, or three. And it's gonna tell you how many pixels are are there per point. All right, the most important rectangle in all of UIView is bounds, it's var on UIView to CGRect. It tells you the bounds, the coordinates of your drawing system, the origin and the width and height in your own drawing coordinate system. And one thing you have to understand is, different views have different coordinate systems. Okay, they each have their own coordinate system, this bounds is in yours. So when you're drawing, you always use bounds. Now there are a couple of other things like frame. Var frame, sounds a lot like bounds. Frame has nothing to do with your drawing. Nothing, it's a Rect, but has nothing to do with your drawing. The frame is where you are in your superview. So frame this not even in your coordinates system, in it's your superview's coordinate system. Says, where you are? Similar with center. Center is not the center of your drawing area, it's the center of view in your superview. So frame in center are, where you are? Bounds is the place you're drawing. Don't get these things confused. I have a slide down here, go through it quick, we never use frame or center to draw because it has nothing to do with drawing, it has to do with positioning. And you might think that the frame and the bounds are gonna be the same size, but views can be rotated. And if you imagine rotating your view, the bounds get rotated with, okay, so they stay the same size, but the frame now has to get bigger. To totally enclose this because it's a diamond shaped, right, View B is a diamond shape. Okay, so the frames' sized-out width and height are not the same necessarily as the bounds' sized width and height. Don't ever think of them as the same. If you ever use your frame to draw in this class, you'll get dinged, that is just wrong. All right, so now we know the bounds of where we are drawing. We know we have this coordinate system in the upper left. How do we create one of these views? Okay, we wanna create one. Well, I told you that you mostly do it in Interface Builder, but if I have a custom view. Let's say I have a custom view, draw something special to just me. How do I drag that out into Interface Builder? Right, Interface Builder's got that nice list in the lower right corner of UIButton, UILabel, all those cool things. My view's not gonna be there, right? So how do I make one? Well, it turns out you drag out a generic one. Currently, as of this my speaking, it's the second from the bottom in that long list. It's called UIView down there. You drag a generic one out and then you're going to inspect it, but you're gonna use a little different inspector in the upper right than you usually do. It's not the Attributes Inspector, it's the Identity Inspector. I think it's the tab, just one to the left of the Attributes Inspector. And in there, the top thing is going to be the class of the thing you're inspecting and it's gonna be UIView when you first drag it out. And you're just going to change the pull down there to pick your class. So now you have a view there, but it's of your class. That's how you're gonna create one of your custom views. Now, the other way you can create views is in code. And again, I'm gonna ask you to do this both ways in your assignment three. And you do that just by calling the frame UIView with frame initializer that we talked about before. You can also set UIView open parenthesis, close parenthesis, then your frame will be 0000, upper left, no size. And you can then just set that frame var, that I talked about a couple slides ago, to position and size, this view in the super view. Okay, just always remember the frame, though, is in the super view's coordinate system, it's saying where you are. Nothing to do with your drawing, just where you are. Okay, so here's an example. I'm gonna create a UILabel encode. Okay, UILabel, you know what that is, right? It shows text, that's the thing that said flipped colon 0, that was a UILabel. Of course, UILabel inherits from UIView. It's a rectangular area on screen. All rectangular areas on screen are UIViews. So, I'm gonna create a rectangle to say where it's gonna be. This rectangle is gonna be in the super view's coordinate system. In this case, I'm gonna put the label at the very top level. In other words, the black view of Concentration, I'm putting it right over that level. So this 20 across and 20 down and a width of 100 and height of 50, that's in the black views, the top level views coordinate system. And then I created a UILabel using that frame. I set the label's text to hello. Nothing's happening on screen right there, yeah. Now, I need to add it as a subview of that black view, so I'm gonna assume this code is in a view controller and I'm gonna use that very special var view. And say, view, add subview this label. Now that UI label gets added as a subview of the top level view, the black view in concentration, it's white here, and it gets put at 20, 20, 150 in that top level views coordinate system. Remember, upper left is 00. So, that means down from the upper left corner. Everybody got that? So that's how you add view, it's very easy just add subview. That's how you put view on screen on code. All right, so when would I wanna create my own UIView subclass versus just using UIButton or UILabel? Those are UIView subclasses. Well, if I obviously want to do custom drawing or if I want to handle some custom touch events, pinches or something like that. I'll talk about handling touch events on Wednesday, but today we're gonna focus on drawing. So we're going to do all drawing today. So to draw, it could not be easier, there's only one way to draw, okay, in all of iOS. Which is you override this function in UIView and implement it. That is the only way to draw. You can't call any functions that draw or anything outside of this method, it's the only way to draw. So that makes really simple, you don't have to worry about any other mechanism cuz this is the only one there is. And so inside this draw method, you're going to draw in your bounds, whatever custom drawing you do. Okay, never call this method ever. This method, you override and implement your drawing, you never call it. If you want your view to be redrawn, because something about you has changed, you call one of these two methods on yourself, setNeedsDisplay or setNeedsDisplay with rect. Okay, and that tells the system, hey, my view needs to be redrawn, please, redraw it. So, only the system, only iOS calls your draw and you can make it call it, or tell it you want it to call it anyway, by calling setNeedsDisplay. Now notice this rect, see the draw rect. What is that rect argument, both up on draw and down in set needs display. That is purely an optimization rectangle, okay. That's if, for example, you had another view on top of view and it went away and it exposed a little rectangle of view, the system would call your draw with just that rectangle. But you are allowed to redraw your whole view if you want. But if you can be efficient about only drawing the rectangle that was exposed, then you can be efficient. So it's purely an optimization. You can ignore it if you want, just draw your whole view. So if you have a simple view that's easy to draw, doesn't use a lot of resources and not 3D graphics or something, then you can just ignore that rect. All right, so how do I implement this draw rect? Okay, I overwrite it now I want to draw. So how are we gonna do it? We're gonna do it using core graphics, this underlie drawing level layer. And the basic way to do it is, you get what's called a drawing context and you ask that context to draw lines and stuff, okay. Now, there's also an objects orientated way to draw with a class called UIBezierPath. UIBezierPath, same concept. It's just putting a little bit of an object or in the way you can build an object that contains some drawing. Which is nice if you wanna repeatedly draw that object, maybe with different colors, whatever. So let's talk about the fundamental core concept of drawing in core graphics, how do we do it? The number one thing about core graphics is that it's context based, so you have to get a context. UIBezierPath will automatically get the context for you, but if you don't use UIBezierPath, you have to get a context. Now, in drawRect, you can you use this Swift global function, UIGraphicsGetCurrentContext and it will give you a context to draw, okay. But there could be other contexts, printing or drawing on and off screen buffer, I'm not gonna talk about those, but there are other ways to get context as well. But when you're in drawRect, it's easy. You just call this one global function, it will give you the context you are currently drawing in. Okay, once you have the context, now you use the context to create what are called paths. Paths are just arcs and line to's and then move to's, which is like jump over a little bit. It's just a combination of all those, that is what a path is. So you're gonna build some path. Then, you're gonna set a bunch of drawing attributes, like the color you want to draw in, any fonts, kind of. That's a drawing attribute, we'll talk about fonts in a bit, but line widths, textures, things like that. You set all those things up and then you do one of two things with your path, you stroke it or you fill it. So, stroke it means draw a line along my path, you know, with a certain line width and color, and fill means fill in the area that my line encloses. Believe it or not, this is the only way to draw, basically, in. Core graphics and you might like, wow, that seems really limiting. All I can do is arcs and lines and fill them in and stroke them, but it's amazing what you can do when you build on top of that primitive powerful mechanisms, okay, and we will talk about that. So UIBezierPath does the same thing, it just does a lot of it under the covers but it has methods for setting line with, and things like that. It has method to stroke and a method to fill, and a methods to do arc to and line to and all that thing. So it's just a kind of arbitrated collecting bag of that. So let's look what the code looks like. To draw a triangle using this mechanism. So first time,I will do with UIBezierPath. I'm gonna create UIBezierPath,it has a lot of initialization but I'm gonna use the one that just create an empty path. That create a new, there is nothing in it. Now I am gonna move around, I am gonna start by moving to a point you see that 80 across and 50 down. Let us assume that this screen is 160 points across and you know 300 or 400 high. So I am moving halfway across and 50 points down from the top my margin is up on the left. Now am gonna add a line down to 140 over, almost to the edge of the screen up there, and 150 down then I'm gonna add another line. You see how I'm just calling functions on my UIBezierPath to add lines, right? So there is another one over here. Then I'm gonna close my path so there is a method in UIBezierPath called close which draws a line back to where you started, whatever that was. So, I'm gonna close my path. And there's a lot of tons of methods in UIbezierPath you have to go look at the documentation. But here's a simple, those are simple method usage, right? Now I kind of been tricking you because you'd look at this and it looks like as I called these, it draws on screen, but no. I just wanted to kind of give you visualization what's going on. In fact, when I did all that, nothing happened on screen. There was absolutely nothing happening, all I was doing was building this UIBezierPath object up. If I wanna appear on scree I have to set my drawing attributes and tell it to stoke or fill. So let's do that. Now we set our colors both our fill color and stroking color using UIColor class. So we don't set it on our UIBezierPath, we actually use color and we say things like UIColor.green.setFill. Well, green is a static var on UIColor, meaning it's a class like a class color, just like get unique identifier was in card. And it just gets the green color and it has about 10 or 12 predefined colors, red, green, blue, magenta, cyan, that kind of stuff. We could also make colors. We could use a color literal there. Remember when we did the color literal? Perfectly legal to say colorLiteral.setFill. So we're setting our fill color and our stroke color. So we're gonna fill with green. Our triangle is gonna be filled with green and the line is gonna be drawn With red and I'm also going back to the BezierPath and saying, set the line with two three points wide on a high resolutions play that will be nine pixels wide. So it's a very thick line, not that thick that but, so now that I have set these things up now I can stroke and fill. So let us first fill And when I feel I get this notice there's no line around the edge I just get the fill of my path that I made. And then when I stroke now I get the red 3 point wide line around. Yeah? >> Where this is written? >> Where this is written? Like where you put this? In your draw rect. In that draw method, that we override in UIView, that's where this code goes. >> So, when I drag a UIView out to the storyboard, do I have to make an outlet to, like, have- >> Okay, so the question is, when I drag a UIView out into my storyboard and I set it to my custom class, do I have to like do an outlet or something? No, you just, in your custom code, implement your draw with that kind of optimization rect argument and put this code in there, and every time the system has your view on screen it will draw this. Now it's very efficient it's only gonna ask you to draw once unless things change, but it's gonna draw it for you. So all the whole, all the drawing is on demand, right? The system asks you to draw, it calls your function, you implement this code and it draws on demand. You never force draw. The only way you can kind of force drawing, is that setNeedsDisplay. And even that's just telling the system to please draw me as soon as possible, but not instantly. It's all on demand drawing. So, this is how you would implement. This code would be in your draw rect. We call it dra_rect, it used to be called draw rect. Now it's called draw under bar rect, but I suppose we can call it the draw method, but we call it dra_rect, it's got that rect argument, okay, that optimization. All right, now you can use UIBezierPath also to create some very common paths like a rounded rectangle which we'll use in our demo cuz we're gonna do a playing card, which has a rounded rectangle. You can also do circles, ovals, things like that using other initializers of UIBenzierPath. Note that you can use your BezierPath to clip your drawing. You'll definitely want this for your homework, pay attention here. So what that means is you draw some shape like your triangle. And if you send add clip to your path, now all future drawing will be inside that triangle. Even if you draw outside, anything outside gets clipped off. So that can be really convenient for your homework. That's my hint for you. You can also do hit detection like is this point in the triangle, right? Using this contains point method and a lots of other stuff. So check the documentation for UIBezierPath of the things that you can do. All right, let's go and talk about UIColor, right? Okay, first setting the fill and stroke something like that. We know a lot of ways to create colors. Color literals, also the static function I mentioned. You can also create them with initializer to take RGB values. Or HSB which is hue saturation and brightness or even a pattern. You can create a color that's actually a pattern some image, just like repeated, right, which is kinda cool. And we also know that views have the background color, right? We set the background color of our base view in Concentration be black. We set the background color of our buttons to be orange, so there's a var for that of course, in view. Colors also, importantly, can be transparent. A transparent color you can kind of see through it a little bit and we specify transparency using something called alpha. How many people have heard the term alpha drawing? Okay, so most of you know this, great. So, we use alpha. Alpha of 0 means fully transparent, and alpha 1 means fully opaque and we can have any number in between. The way get a transparent color, is take a color you've got, like yellow let's say right there, and you send it the message, or the function, withAlphaComponent. And it will give you a new color that has that transparency. Now you can draw with that transparency. If you want to draw in you view with transparency, though, be very careful. Because, the system assumes by default that your view only draws opaque. It only draws with fully opaque colors. If you actually draw with transparent colors, you have to set a var on the view called opaque to false. Now why does he do this? Well, imagine how much more CPU and GPU intensive it is to draw with transparency because now the views behind you have to be composited with your bits, and you know the compositing not cheap, all right? The GPU got a lot of work to do there, so that's why it says to assume everything is opaque and that everything overlapping doesn't have to be composited. And, so if you wanna draw with transparency, which is fine. It's legal. It's not that expensive that you would never wanna do it. But just make sure you set this opaque to false. And you can do this in interface builder. You can click on the view, and one of the things in the inspector over there is whether it's opaque and just turn that off. By the way, you can make your entire view transparent, if you want, by setting it alpha, so views have alpha. That means all your drawing in there will be somewhat transparent. That's kind of a cool feature. All right, brief mention about layers. So UIView's drawing mechanism, is actually built on another whole system I'm not gonna talk about, called Core Animation. And that's because everything you do in a view can be animated. Views moving, transparency happening, all this stuff can be animated and there's a whole layer for doing that. And so, the UIView, we see a top level API for it, but there's actually a whole other API for drawing called the CALayer, Core Animation Layer API. And I actually mentioned this in the hints of Assignment 2, you've already seen this. There's this var in UIView, it's in button because button's a UIView, called layer, which gives you the layer, the CALayer, that's being used. Now I'm gonna talk all about animation, hopefully next week, maybe the week after, and we'll get into some of this stuff. But you already know from Assignment 2 that there's a few really nice vars in the CALayer like cornerRadius, if you wanna put a rounded rect on your view, or the borderWidth and borderColor. By the way, you notice that the borderColor there was a CGColor, not a UIColor. CGColor means it's a Core Graphics color, why is that? Well, that's because this whole layer mechanism, Core Animation, is below the UI kit layer, and it built on top of the Core Graphics layer. So it can't really use the UIColor struct because it's kind of above it in the hierarchy of frameworks depending on each other, so use a C, CGColor. Luckily, UIColor is also above Core Graphics. So it has a var called CGColor, which gives you itself as a CGColor, and I showed all that in the hints of your assignment, right? Okay, so that's a layer thing, we'll talk about, more about animation later. Let's talk a little bit about the transparency and how that works, like which views are in front of which other views, right? If I have a transparent view, and it shows the one behind, which is behind? That's all determined by the order of the subviews list. Any view that has subviews has an array of them. The first thing in that array is the back. And everything else is one step in front, the last thing is the one in the very front. So if you have multiple subviews of your view and they overlap, it's the one in back is the first one in the subviews list. So you can reorder the subviews list and move them. Move the one from the front to the back or whatever, so you can control that transparency effect. Also, even if there's no transparency, if the views are on top of each other and they happen to overlap. A lot of times when we have multiple subviews, they're are all side by side, they don't overlap, but they could. Oops, they could overlap, and if they did, then that subviews will tell you the order, which ones are in the front. You can completely hide a view without taking it out of the view hierarchy, with isHidden. If you say to a view, this is a var on view, if you say view isHidden equals yes, it will not appear on screen. It'll still be in the view hierarchy, still in the subviews list. It'll still have a superview, but it won't there, and it won't get any gestures either. So you won't see its drawing, you won't get any gestures, it'll be hidden. And you'll be surprised how common it is to wanna do these. You put a view in the view hierarchy somewhere, you hide it, then when some condition is true, it appears, probably animated. Things appear, that's a common way to build an interactive UI. All right, so we know how to draw with these arcs. What about drawing text? Well, actually text is just some font designer, moveto, lineto, curveto, okay. That's all the character, every character in the text is just a bunch of little moveto, lineto's. Luckily, some font designer somewhere did those all for you, or that would be incredibly tedious for you now, wouldn't it? So we wanna be able to draw though using these wonderful moveto, linetos, which are the glyphs in a font. So how do we do that? Well, before I even dive into that, remember that UILabel draws text real well. And there's no reason you couldn't make a UILabel be a subview of your view, and thus draw the text that way. Okay, that's a great way to draw it, because UILabel's got all the incredible amount of center text alignment, and you can control the color, you can put an attributed string on that label. You've got complete control with that. The only thing is you have to make sure that you keep that UILabels frame in the right spot in your subview. You know, as, it's one of your subviews, so you've gotta make sure its frame is kept in the right space. But that's a great way to draw text, so don't forget that. But I will talk to you now about how to draw in your draw rect, and use attributed string. Okay, so you just create AttributedString, and you send it the message draw, and it will draw at the point you say in your current context. So drawing text could not be easier, you already know how to do an attributed string, so you just create attributed string here. My attributed string doesn't have any attributes here for slide space issue. But you could put your attributes on it, whatever, and then do draw(at. You can also do draw(in), and it'll draw it in a rectangle in the upper left corner of the rectangle. And then you can also get the size of the text by just asking the attributed string, what is the width and height necessary to fit this attributed string. So super easy to draw text in your draw rect, you just use AttributedString. Now I wanted to take a little time here about AttributedString and circle back to what I said about it before. Remember I talked about how NSAttributedString is an old objective thing, and it uses NSString and then, we're using String? And String uses kind of a different encoding for Unicode characters and sometimes mapping between NSString and String. The indexing is different because we got wacky characters like emoji, that are actually multiple Unicodes, ugh, that whole mess, you remember that? Well, I'm gonna tell you a little bit how to get around that. You don't need it, you didn't need it for Assignment 2, you won't need it for Assignment 3. Maybe I'll ask you to do for Assignment 5. But if I do wanna set attributes just on certain characters in my AttributedString, and it's a String, not a NSString, how do I do it? Well, the answer is, this class right here, NSRange. So NSRange is the way you specify a range into an NSString. Okay, so this is an old Objective-C way of doing a range. In our world, we do Range<String.Index>. That's how we index into a string, right, because we know the strings are indexed by String.Index not by init. So we have this totally different thing, Range<String.Index>. And here AttributedString wants to do an NSRange of init because NSString was indexed by init. Okay, so how do we get from range of String.Index to NSRange of an init that matches, okay, so that the NSAttributedString matches our string. Well, we do it with NSRange, we do it with this right here. This is NSRange initializer, you see it, NSRange initializer. It takes this firstWordRange, which is an Swift range of string.index. You see it goes from startIndex here over to indexOf, so it's a range. It's got the little ..< which is our nice range syntax there. So it takes a range and it converts it into an NSRange, but it needs to know the string that that string, those string.indexes are into, right? So the combination of knowing the string and the string.index range. It can crunch on that and change it into an NSRange that will work in an NSAttributedString. So that's the magic. NSRange, use the initializer that takes a range of string.index and a string. It'll give you back NSRange. Now you can call methods like in AttributedString, like addAttribute, which adds a single attribute at a certain range of characters. Look at the argument, range, nsrange, now you have an nsrange to give it. All right, fonts. Okay, fonts incredibly important in iOS. I cannot overstate how important fonts are to the look and feel of iOS. These are, I think are iOS 10 screenshots, and iOS 11 things have changed slightly, but not too much. But even in these screenshots you can see how important fonts is. They really make a difference to how your UI looks. So you have to pick the right font. So how do we pick fonts? Well, in iOS, the most important concept with font is preferred fonts. There are about ten categories of fonts like body fonts, headline fonts, caption fonts, footnote fonts. So there's these kinds of fonts, and you need to pick one of those preferred fonts for whatever environment you're text is in. If you're showing main information then it's probably the body font. If it's a little caption of an image, you're gonna use to caption font. Okay, so you got to pick the right font and use it. Now this is really easy to do Interface Builder. You just pick your label, or button, or text field, or whatever you're doing, and you go to inspector, and where it says font, instead of using system font, which is what we've always chosen, because I haven't talked to you about preferred font, you choose one of these things, body font, caption font, footnote font, etc. In your code you could do the same thing, and you do that using this static i.e class type method in UI font, called preferred font for text style, and that text style is headline, body, footnote, whatever. This will give you back a UI font that you can put in your NS attributed string dictionary. One of the keys in there is the font, and you can use this as the font, when you're drawing text in your drawRect. Now one thing to be concerned about here is that the size of this font is variable. But you get old people like, okay, we're starting to hold our phones like this, as the years go by, cuz our eyes are giving up, so we like the fonts big. Then we can hold it all closer and see our fonts. Well, the way we do that is we go into our settings on our phone, and one of the settings under general there is accessibility, and we can say larger text, and we move the little slider, and now all the fonts get bigger in all the apps, but only the apps that play this game right, and use preferred fonts. So you wanna be one of those apps, otherwise, you're gonna lose the older folk from your demographic, of your people buying your app. So it's important to make sure that your UI also works when fonts get bigger and smaller, and the number one way to do that, auto layout. You know that auto layout where you put constraints, pin things to the edges. You wanna use good auto layout so as fonts get bigger, and the text field gets bigger, other things push out of the way and it uses the screen real estate properly. So you've only seen a little bit of auto layout. I'm gonna show you more auto layout at start of Wednesday's lecture. You're gonna see more and more as the quarter goes by, but that's a good way to make it work. Now what if you either wanna use some other font, some special font that's something that's maybe part of your marketing or whatever, or you want it to be bigger, or smaller I guess, than the standard size font that comes with a certain prefered font. Okay, well, UIFont does have a way to create a font by name like if you want Helvetica 36 point, you can say UIFont(name: "Helvetica", size: 36.0), and there's also a class called UIFontDescriptor. Has a lot of cool ways where you can take a font and say give me the Bold version of this, give me the Italic version, okay all of that stuff. So you can get your font this way. But if you do your fonts this way, you still want, when I go into my setting and make my font bigger, you want your font to get bigger, and look what font size this is, 36. It's fixed. So I need to somehow scale this font up if the user has put their slider to bigger, and the way you do that is with UIFontMetrics. So you create a UIFontMetrics object for the text style body, footnote, caption, whatever, and then it has very cool method called scaledFont, and you give it a font, like Helvetica 36, and it will give you back a new font, Helvetica 42, maybe. It's scaled based on what the user said. Okay, don't skip this step, otherwise, if you use a custom font, when people move that slider, your app's not gonna work, and people are gonna be like, I hate that app. I can't see any of the text and it's too small. There are also system fonts. We use those so far in our concentration demo. That's only for things like button titles, and stuff like that, that's not for user content, stuff that the user has generated, or requested, or something like that. Thos are preferred fonts. System fonts are just like buttons, things like that. What about images? We know how to draw lines and arcs. We know how to draw text. What about drawing images? Just like UILabel for text, there's another one called UIImageView, which lets you add an image as a subview. So you could use that to draw an image in your view if you want it, just do it as a subview. But if you wanna draw an image in your drawRect, you can do that too. You need a UIImage object. A UIImage represents an image, jpg, gif, whatever kind of image, it represents an image. Now, how do you get an image? There's a bunch of ways to do it. One way is to drag the jpg file, or whatever, into that Assets.xcassets file. Remember when we're doing our demo, and I put some things off in supporting files? One of the things I threw in there was the place where the app icon was. Well, you can drag other images in there, and along the left hand side will be all the names of them, and then you can call this method right here, UIImage named whatever, and it'll go look in that assets thing, and find an image with that name. Now, this is a failable initializer. It can return nil, and that's because it might not find that particular image in there. So you have to say usually if let to do that. How else can you get an image? Well, you can get one from the file system. You've got a jpg in the file system. I hadn't told you how to access the file system, so you're not gonna be doing that. I'll show you later. You can also get it if you have a bag of bits that you got over the Internet. The data that has a bags of bits with jpg data in there. UIImage knows how to look in that bag of bits and figure out if it's an image. You can even use this global function, UIGraphicsBeginImageContext, and then draw arcs and lines, and it'll capture it as an image. So you can even draw a custom image if you want. So anyway, you do one of these things, and now you have a UIImage in your hand that represents the image you wanna draw. How do you draw it? Exactly the same as NS attributed string, you just use draw(at), which will draw the image with its upper-left corner at that point, but you also can do draw(in rect, and it'll scale the image to fit in that rect. So that's a cool way to scale your image up and down, and you can do drawAsPattern which will tally your image repeatedly, repeat you image to fill the rectangle. Super easy to draw images as well. All right, let's talk about your bounds changing. I've got my draw rect. It draw beautifully, but now my bounds changed, and when will this happen? Well, number one way, your device got rotated. They were looking at you in portrait, they rotate you. Now you also went from this tall, thin view, now you're this wide, very short view, okay, your bounds have completely changed. You went from a width of maybe 200 to a width of maybe 700, and from a height of maybe 3 or 400 to a height of 150 or 200, so that's a major change. You're gonna have to really redraw all of your stuff, usually. Unfortunately, that's not the default. In iOS, when you're bounds change like that, it does not redraw you, believe or not. It takes all your bits And squishes them. So looks absolutely terrible most of the time. To smashes a bit, squishes them down, stretches them out to fit your new bounds. Which you almost never want that. So how is that control? That's control with a var in UIView called contentMode. Okay, and the contentMode's just basically saying, what kind of content do I have? Do I have the kind of content that can be squished and scrunched like that, and still look good? Or do I have the kind of content where I have to be redrawn when my balance changes? So, the three different categories of contentModes. One is, keep my bits, okay, don't redraw me, but just move my bits, unscaled, to a new position, upper left, top, lower right, keep it in the center, whatever. This one not almost never get used. Then they're scaling the bits which is scaleToFill as the default where it just scrunches the bits to fit in the new space. It doesn't even respect the aspect ratio. But you could set the contentMode to scores the bit but keep the aspect ratio the same size so that a face doesn't go from, you know, an over as way to a tall over or whatever. But the one you guys probably are gonna want most of the time is ContentMode redraw. And what that means is, when my bounds change, call my draw Rect and let me draw myself again, and that's probably what you want. Could you draw rect knows what your bounds are and when it gets called, it can draw something appropriate for the bounds you read. So like in your homework, for example, well maybe your homework is not a great example because probably, the big views that are changing bound it's your subviews that you are gonna want to lay out, which I will talk about again. But if you did have a thing where you are drawing something in a view, like maybe we are doing our cards and concentration. And we always want them, to be a certain aspect ratio, whatever, we could redraw, in our draw Rect or whatever, it's probably not a great example. In fact, let's go on to the next example which is, what happens if I have subviews and my bounds change? Cuz this is gonna happen in your homework. You're gonna have some views, you're just gonna have a subview which is a lot of cards, your set cards. Because in assignment three, now there's no limit on the number of cards that can appear on screen, right? In assignment two, we limited to like 24 cards as the most. Now no limit, so you're always adding more cards. So, you're gonna have to use, put them as subviews of some view. You can't stack view anymore, in other words. So what happens when your bounds change there? Well, when your bounds change, you're gonna get sent this message layoutSubviews. And that's an opportunity for you to go reset the frames by just changing the frame var on all of your subviews, okay. So, you're all, if you have subviews, you're almost always gonna implement this method. And don't forget to call super to do it. Now, what's the one time when you're not gonna implement this method? Is if you have subviews and you have autolayout on those subviews, okay. If you have autolayout on your subviews, then the Autolayout constrains will determine where the new frames are, okay. So this would only be for views where you're not using autolayout. that might be true of your assignment three, whatever view contains your card, probably not gonna use autolayout. It will be almost impossible to write constraints that would work for laying out an arbitrary number of cards in arbitrary bounds, okay. You're probably gonna wanna do that in code. Okay, so when your bounds change, you've got two different things to think about. If you have any subviews, layout subviews or autolayout. And if you draw something, then you've gotta think about your content mode on whether you wanna be asked to redraw. Okay, so that's it for the slides today, I'm gonna into demo here. The demo I'm gonna do is a playing card. So this is gonna be a new app, and it's just gonna draw a playing card, you know, like jack of clubs, six of hearts, whatever, it's gonna draw that card. We're gonna draw that custom, by ourselves. And today, all I am gonna do in the demo is the model of that MVC that draws the playing card, which is gonna be a playing card and a deck of playing cards. And the reason I'm gonna do that is I wanna show you enum. You haven't grab a chance to see me demo enum. So we're gonna use enums in our implementation of our model. On Wednesday, I'll continue with the drawing part of this demo or I'm gonna draw this playing card. And then I'll do some slides on multitouch and then we'll add some multitouch to the playing cards, swiping to go to the next card, we'll do tapping the flip the card over, that kind of stuff. Okay, your assignment two of course is due on Wednesday as you know. Assignment three will be assigned on Wednesday. It's just to make your set game custom cards, okay, drawn with a custom view, that's what assignment three is basically about. We don't have a Friday section again this week, unfortunately, due to some scheduling conflicts, but next Friday we will and it'll be on source code management. Okay, let's go create a new app here. Okay, so I'm just gonna go over here, use this same splash screen but I gonna say create a new Xcode project, this has nothing to do with concentration here. As always with the single view app, I'm gonna call this app PlayingCard. That's what it does to show it's a PlayingCard. We're not doing any database, we're not doing testing yet. We're gonna put in the same place I put concentration, we're not doing source code control yet, although like I'm saying next Friday, we'll learn about that. Here's my project, now this time I'm gonna keep my xcassets, right, here's my xcassets where my AppIcon is here. I'm gonna keep that because I'm gonna use some images for the face cards. So you can see how to draw images there. But I'm not gonna use my launch screen or my app delegate here. So I'm just gonna put them again in supporting files. I just like to get them out of the way so they don't really kind of demand my attention. Now, before I even go here and build my UI for my playing card thing, we're gonna go do our model first. So we're not gonna do any UI to start, we're gonna do model first. So how do we create model files? Remember, File > New > File, and we pick this one right here, Swift File which is a non-UI, right, a UI independent thing. And when we do that, we ask for the name, so let's first start by doing a playing card. Okay, now on my model, it's just gonna be a deck of playing cards. So these are gonna be UI independent representations of playing cards, a deck of that. So we'll start with the playing cards itself and then we'll do the deck. So here it is right here, import Foundation, I'm just gonna have it via struct, there's really no reason for it to be a reference type. And it's going to be a playing card. Now, a playing card is made of what? Well, it's got a suit, which I'm gonna have some type for that, and it's got a rank. Right, that's all there is in a playing card, suit and rank. So how are we gonna represent suit and rank? Well, of course, because I wanted to show you enum, l'm gonna do it with an enum. So, let's create an enum for the Suit. And then we're also gonna create another enum for the Rank. Now let's talk about how we would do this. Now, Suit is probably the world's simplest enum, right? It's the case, it could be spades, or it's hearts, or it's diamonds, or it's clubs, that's it. This is probably enough right here. That's really all we need to do for an enum. I'm gonna take this opportunity to teach you a little bit about enum that I didn't mention even in the slides. But it was in your homework reading, so hopefully you got this which is raw values. What are raw values in an enum? Well, it turns out that you can associate a fixed constant raw value for every one of your cases. Okay, now, Swift will even do some of this automatically. For example, if I make my raw value type which I specify by just saying, colon type, after enum, by making being int then it will automatically make this one's raw value be 0. And it's gonna make this one's raw value be 1, etc. Okay, 1, 2, 3, 4, 5. So, it just goes in order, the lexical order which appears in the file. You can also make your raw value be a string, for example. And if I do that, Swift will automatically make this one be spades, this one will be hearts, this will be diamonds. Another words, it makes the raw value be A string version of the case. Now, why would you ever want raw values? Well, to be honest, I think a lot of the raw values support backwards compatibility because in Objective-C enums were essentially ints, right? Zero, one, two, three, four, five. So raw values it's like raw value int. But you could imagine, it might be interesting if there is some piece of data that it makes sense to associate with all the cases. And again it has to be fixed, and constant, and unique. For all the cases but still you could imagine that. For example, I kinda saw that maybe this suit raw, this might be a good one. If I made the raw value be string, maybe having the Unicode character that represents the suit. Kind of be associated with every case, might be valuable. Now, how can you use raw value? Two ways, one, you can create a suit by providing the raw value. And it looks just like an initializer on suits, suit open parenthesis raw value, that's how you do it. And you give it like a heart symbol there, and it will give it back. Now that's a failable initializer cuz you might give it a string like x, that's not one of these four strings. And you can go the other way as well. You can, if you have a suit, like suit.spades, you can say, what is the raw value? And you can get this string so that might be useful too. We don't really care about that in this demo but I just wanted to show you about this raw value business. All right, let's go down to Rank. Now, honestly, if I were doing Rank, it would look like this, case ace, case two, case three, all the way up to case jack, case queen, case king. This is what now, all the ones in between. That's what I would do. But I'm not gonna do it this way cuz I wanna show you associated data. This is mostly for demo purposes, I would probably just do it as these 13 things. That was probably the best representation for a rank. But instead, I'm gonna do it with associated data and so I'm gonna have case ace, then I'm also gonna have a case face, which is a face card. And it's gonna have an associated value, which is a string, which is either J, Q, or K. So there's gonna be a face card that's either jack, queen or king. Terrible representation, at the very least this should be another enum, not a string which can be anything. It's kinda silly, but anyway. And then I'm also gonna have case numeric, okay, for 2, 3, 4, 5, 6, 7, 8, 9, 10. And it's gonna have, of course, associated data int. Now, I could put in here like pips count, a pip by the way is one of these, a pip on a card is just one of these things. So a numeric card has 2 pips on it, 2 has 2 pips, 3 has 3 pips and so on. So I could put pipsCount here as documentation. But you know what, you actually don't wanna do that if it's completely obvious what this would be. And if I had a numeric rank, it's completely obvious that this is the number, so I do not need that. Now, it's not very obvious what this is but as I already told you, this is a bad representation anyway. But I just wanna show you what it looks like to get the associated data out of here and use it, etc. For example, rank is an enum, it can have funcs, I could have funcs and vars on here. So I'm gonna have a var called order which is an int, which is gonna return the, which position the rank is in the order. So I'm gonna switch on myself and if I am an ace, I'm gonna return 1 cuz that's the first, that's card number 1, right? If I am a numeric, then I'm going to get my number of pips. And I'm gonna return the number of pips. Cuz if I'm an numeric card, then however many pips I have, that's my number. And then for face, I'm gonna say case .face, and I'm gonna, whoops, get the kind, Q or J, or K. Now, I'v got the kind, I guess I could say, I'm gonna go to the next line and say, if the kind equals J, then return 11. And then I guess I could go else if the kind. Okay, but I'm, this is getting terrible. This is gonna be ugly, awful code. But turns out there's a lot better way to do all this which is to go here and say. If it's face where I get the kind where kind equals J, then return 11. So this stuff with switch is actually a pattern matching language, which you can go and learn about. But one of the things it's able to do is where. Where it can kind of narrow this down a little bit. So now I could do that for all three here, the Q and the king, and return. That's why you can see why I use this terrible string representations, so I can show you where. Now, notice when I do this, I still get this complaint, switch must be exhaustive. And it's like, what? Look I do ace, I do numeric, I do face, how come it's not exhaustive? Well, because where makes it so these are not counting every possible combination of where. It could be, the kind could be something else. So, of course, I have to add here a default break, or actually I have to return something, so I'll return 0. This is also bad design. This probably wants to return nil, and this wants to be an optional. That would be better design, I'm not gonna do it that way. But just so you know, that's bad. I could also have static vars here, maybe I have a static var, which is all the ranks which returns an array of every single possible rank. And I'm gonna have to kind of build that up cuz I have all these weird data structure here. So I'll create something called allRanks which could be a type array of ranks. And let's start it out with and ace, notice here by the way that I have to type this side here. It can't really deduce this. If I took this away right here, let's get these other warnings off here, I'll return them allRanks. If I took this typing right here, this static typing away, I'm gonna get an error that it can't infer this type. And that's because there might be another enum that has a case of .ace. So how does it know the, which array of enums this is? That's why it's saying type expression is ambiguous without more context. So I can add more context by putting that type. I could also say rank.ace, that would add more context. And now it knows it's an array of ranks. So now let's also do for pips in 2...10, remember dot, dot, dot without the less than means including the 10. And now I can say, allRanks.append, Rank.numeric with that pips. So now I've added all my numeric ones, and then maybe for the thing, face card ones I'll just say allRanks plus equals an array that has rank.face, where the kind is a J. And then I'll just copy and paste, copy, oops, paste, copy, paste and we'll do the Q and the king. By the way, if I do this, I only really need to put this Rank dot on the first one and it can infer the rest of them. Dots though means dots. So I don't need rank dot, because it figured out pretty quick this is an array of ranks. These are all ranks, so it works. I could do the same all thing up here by the way, too. I could have a static var all. I make it static because I'm getting all of them here. I'm not talking about a specific one so I want them all. So this would be, we'll do the same thing here. A rank of suit.spades, .hearts, .diamonds, and .clubs, oops, hello .clubs. So this is kind of nice to have this static bar which gives me all the suits, all the ranks. That's gonna make it a lot easier actually to create our playing card deck so let's do that. So we've got this nice representation, not so nice, but somewhat nice, representation of a playing card. Let's go ahead and make a deck, so I'm gonna make a New File. A new thing here it's gonna be a PlayingCardDeck. Here it is, struct also no reason for not to be PlayingCardDeck. And, what do I wanna playing card deck. Well, of course I'm gonna need a var which is the cards in the deck. Right, so I'm just gonna make that V an array of playing card, probably want this to be private set. Okay, where I control the cards because it's gonna be start up being a full deck. And then I probably want some function, like draw which grabs a PlayingCard out of there. So that's how you take a playing card out of the deck. So how would I implement drawing the card out of there? Well, I'm just gonna say, let's see what I remember, I think I used something from Concentration to do this. Check I did. Yeah, I did. Okay, so here, I'm just gonna see if I have any cards. Then I'm going to return cards.remove at: hover many cards I have .count.arc4random. So, I need to go over to Concentration. Here's concentration down at the bottom here. We have arc4random. So I'm just gonna grab that out of here. Put that in here, so I can use arc4random there, if I don't have any cards left then I'm gonna return nil, so I better make this draw our return an optional playing card. Okay, so that way people can't add more cards to my deck I just start out with a full deck. Okay, I get start out with a full deck so I better have an init that does that, init is really easy to write given our nice plain card structure, sorry, this warning here, cannot use mutating member on immutable thing, everybody knows why that is. This is a struct, it's a value type, this mutated by removing a card, so we have to say this is a mutating mark, another good example doing that. All right, so here I'm just gonna go through all my suits for suit in Suit.all, and for rank in Rank.all, okay, then now all these alls that we did, but of course look where I put suit, if you look at PlayingCard over here, I put suit inside Playingcards. See I declared it inside, so Swift allows you to nest types, put types in other side. Which makes perfect sense here, because Suit only makes sense in the context of the PlayingCards, it just makes perfect sense. But it does change the name of the Suit, it's no longer called suit, it's called PlayingCard.Suit. Playing Card.Suit and this is PlayingCard.Rank, PlayingCard.Rank. So nesting just changes the names of things and the accessibility can do private and stuff like that you might not be able to see it. So now that I have that I can just have my cards append a playing card, now PlayingCard since it is a struct is going to get this automatic initializer, you see it right there, so I'll double click on this to get that. And the Suit is just gonna be the Suit that I'm for in-ing and the rank is the rank I'm for in-ing. So now you can also see the nice context here and the nice syntax of for in when this is an array. These are arrays. We're just going through each element of the array, right. Remember that these are arrays right here? See, this is an array for Suits. This is an array that we made for Ranks. All right. So that's it. Now, last thing I'm gonna do here is just print out some cards in my deck to make sure it's working. Kind of a cool place to put testing code is back in your View Controller. Okay, if you look at your View Controller you got this two frame methods. I'm gonna get rid of that one. But I'm gonna keep this one which was mentioned in your homework. This is, viewDidLoad is a great place to like initialize your View Controller. And also just put like debugging code, checking things out, things like that. So, here I could do for example, let's just print out 10 random cards. I'm gonna say for under bar, cuz I really didn't care about the index, in 1...10 cuz I want 10 cards. I'm just going to let card equal a playing card deck, so I need a playing card deck, var deck = PlayingCardDeck. So I'm just gonna let the card = deck.draw. Now, this is going to be an optional playing card right here, right? Optional playing card because the deck might be empty, so let's go and say if let, although I probably could put exclamation point cuz I'm only grabbing 10 cards. I know there's 52 cards in a deck, but we'll do if. And now I'm just gonna print this card out. And I can print a card by using this backslash parenthesis. Swift knows how to print out things that have, we'll see what it tries to print out when we, when we do this here. So let's try that and see what happens. So I have no UI's, so this could be a blank UI but it's gonna print this stuff out on my console so I really only need to just look at my console here, Right, so there's my thing here, so here it is printing it out,and it prints out 10 random cards, kind, ugly looking right here, it prints out all the vars and etc. One thing that's really cool that I don't have time to show unfortunately, cuz we've already gone over but I'll just mention it, is that you can make it so that something prints out really nice when it's got that parenthesis, backslash parenthesis, by making it implement the custom string convertible protocol. So if we made playing card implement custom string protocol, the only thing that's in that if we do fix it, we'll see what it is, is this var description and you can return, like here I could return for example a combination of the Rank and the Suit, and then I can go make custom string convertible for Rank and Suit down here. Comma CustomStringConvertible. Implement the description in there as well. Okay, which I'm trying to do, so we will go here, we will put description, we will go here, we will put the description, and we would implement these two vars, and then when we print out, it would print this nice string with two nice strings with that. Now, we wouldn't get all that really verbose printing out. So CustomStringConvertible is a nice protocol to implement if you want things to print nice in the console. Okay, so that's it. I will see you on Wednesday. We will dive into doing the M or the V and C part, which is to draw a playing card. And we'll learn all of that custom views and all that, I know there is more to touch after that. See you then. >> For more, please visit us at stanford.edu.
Info
Channel: Jakub Moravčík
Views: 2,333
Rating: undefined out of 5
Keywords: paul hegarty, developing ios 11 apps swift, developing, apps, ios 11, ios, apple, swift, iphone x, drawing, stanford university, stanford, university, cs193p, 2017, swift 4, error handling, throws, any, anyobject, casting, nsobject, nsnumber, date, uiview, cgfloat, cgpoint, cgsize, cgrect, frame, center, identity inspector, path, uibezierpath, uicolor, layers, view transparency, uilabel, font, uifont, uiimageview, uiimage, programovanie, vyvoj aplikacii, vyovoj pre ios, programovanie mobilnych aplikacii, data
Id: 5n7cqFdJd1U
Channel Id: undefined
Length: 87min 8sec (5228 seconds)
Published: Sun Nov 19 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.