[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.