B1 Intermediate 3199 Folder Collection
After playing the video, you can click or select the word to look it up in the dictionary.
Report Subtitle Errors
Stanford University.
Okay, well welcome to Stanford CS193p, Winter of 2015.
This is lecture number five.
And, today we're gonna finish up
what were talking about last time.
And talk a little bit about Objective-C compatibility,
because, of course iOS was originally written in
an entirely different language.
And so there's a little bit of
bridging between the Swift world and the old world.
And we're gonna talk about an example of
using any object which is property lists.
And I'm gonna do a kind of a brief demo even,
showing you that.
Then we're gonna move onto a completely new topic,
which is views.
So, views on how we draw, and how we handle touch input.
In our UI and so we're gonna go over that, and
I'll be having a demo for that as well.
So, I'm gonna blast through these slides pretty quick.
As usual, the demonstration is probably
a little more elucidating then some of the slides, cuz when
you see it in action, it's a lot easier to understand.
So, Objective-C compatibility, so, virtually all the iOS API,
this API that was written over the last, however many years,
in Objective-C, is available from Swift.
And you can't even tell the difference, okay?
It's just kind of, automatically made available.
And, when they designed Swift,
they went to a lot of trouble to make sure that it would,
could be done in a very compatible way.
So, that all of the iOS API, would work,
just fine in Swift.
There's few special data types though, that they've gone
to special care to bridge between the two languages,
and this makes using iOS API from Objective-C, much easier
in Swift, and so, I'm gonna talk briefly about those.
One is NSString.
That is the old way that string were represented,
in iOS, and that is bridged interchangeably to
Swift's string.
You can't really even tell the difference.
If you have a method that takes an NSString,
you can pass a Swift string in there, and vice-versa.
Okay, they're just automatically treated if
they were the same thing,
even though they are quite different things.
String in Swift is not even a class.
Okay, its just a struct.
So, they've done some miraculous work to
bridge these things.
NSArray, which is the old array,
is bridged to array of any object.
Okay, so any time you see, an array of any object in
an iOS API, it's because it was an NSArray before, okay?
Then NSDictionary is an interesting one,
it's bridged to dictionary where the keys have to be
subclasses of NSObject, and
the values are any object, okay?
Now, you might think, that's gonna be a problem, and
I'll explain why that's not in a moment.
And then of course, Int and
Float, Double, Bool, they are all bridged to NSNumber.
We talked briefly about NSNumber,
we talked about the NSNumber format and all that.
So, NSNumber was the old Objective-C object that
just meant, any kind of numeric value.
And, so anything that used to take an NSNumber, you can
pass a double, or float or int in there, and it'll just work.
Of course, coming back the other way isn't automatic,
because it doesn't really know necessarily what you want.
But you can use the methods like double value or
int value, like we use double value, in the calculator.
And of course, int and float and double are also bridged to
the C types int, float, and double.
So, you have an API that takes an int, and hold a C int.
It'll be bridged to take capital I Swift Int.
So, this stuff, all just works completely automatically.
And you don't ever have to do anything.
And it just works.
However, if you wanna be explicit, about the type,
you can cast, okay?
So, it is perfectly legal to cast a Swift string to
an NSString, and it will always work, so
you don't need as?
Okay, it's all a hundred percent guaranteed cast.
And then once you do that,
you have an NSString instead of a string, and so
you can send it messages that are in NSString.
Although you wanna be careful with some of them,
like length, is an NSString method,
that doesn't exist on string.
It doesn't exist on String for a reason.
Because, we talked about this before, you know,
the length of a Unicode string, can vary,
compared to the number of glifs you'll see on screen,
and the new Swift string deals with that, and the old
NSString doesn't really deal with that properly.
So, you gotta be careful, when you start calling NSString
methods that, you know,
there's not, you wanna look for a string equivalent, and
use that if you can, because it's newer, and
it does thinks a little more accurately.
But NSArray is the same thing, you can cast from an array,
that you have in Swift, to an NSArray, and
then you can call them methods in NSArray.
Like components joined by string, which is kinda cool,
it takes all the components in the array,
sends them description, takes those descriptions and
joins them by the string you pass as an argument.
This is the same as the join method in Swift's string.
Okay, so, you can look at the documentation for
string and, NSString and NSArray, but
pretty much all the things you can do there, or most
of them anyway, are available in the Swift versions.
But again, if you're passing them back and
forth, it's all automagic, so you'd say.
Now, you might get a little concerned about dictionary,
because dictionary, the key has to be an NSObject.
And of course, we know that string, and array, and
dictionary in Swift, are not even objects, okay?
They're not any object, or
they're, and they're not an NSObject for sure.
But you can still pass them anywhere you wanna pass in
any object, or a subclass of NSObject, because they
are bridged to classes that inherit from NSObject,
because NSString, NSArray, and
NSDictionary all inherit from NSObject.
So, that means strings, Swift strings,
can be the keys in a dictionary, an NSDictionary,
even though it requires NSObject, okay?
And of course, all three of them can be values,
because, even though they're not really objects,
they are structs, they are bridged to things that can be.
So, it is okay to pass a string or
an array to any object.
You won't even notice this bridge isn't bridging 99% of
the time.
It just happens automatically.
But I just wanted you to know that's going on, so
you don't get kinda confused when you start
seeing NSString, NSArray, what's going on, okay?
They're just being automatically bridged for you.
All right.
So, we talked about any object last time,
and now I'm gonna talk about a use of any object,
which is called property list.
So, property list is not a type or
anything like that, it's just a term that we use.
And the definition of that term is, it's in any object,
a property list is any object, that is known to be,
behind the scenes, one of those six classes.
NSString, an array,
dictionary, number, data and date.
Or bridged too with Swift, okay?
So, why do we define this property list thing?
Its in any object.
Its kind of unwieldy, because you have to do a lot,
a fair amount of casting, ising and asing, cuz
any object, of course, to use it, we have to kinda cast it.
And so, why do we have this thing?
Two reasons really.
One, property list is used to pass data around blind.
It's like a cookie, right?
In a browser.
It's kind of this just bundle of data,
that only the creator of it knows how to interpret it,
because only they know what strings, arrays,
dictionaries, and stuff are in there.
But to everyone else it,
just looks like any object, so they don't know what it is.
So, all they can do, is pass it around to other things.
So, it's good for blind data passing, the other thing is,
a property list can be used as generic data structures, okay?
Since it's got arrays, and
dictionaries, and numbers, and dates, and all that in there.
You can turn a data structure, like your r-ops stack,
in the calculator brain, it has op enums in there,
that's not a generic data structure, but
we can turn it into a generic data structure, for example,
and then do things with it like write it out to disk, or
send it over the network, or things like that.
So, those are the two main reasons we use property list.
And, a really good use of property list in
iOS is NSUserDefaults.
So, NSUserDefaults is like a really tiny little database,
and it only knows how to store property lists.
And it's really for things like settings, and
preferences, stuff like that.
You would never, it's not high performance,
you would never use it for big things like images or
anything like that, just property lists, you know,
dates and strings and doubles, and things like that.
It can store and retrieve any property list, and
it just stores it like a dictionary,
as if the NSUserDefaults thing itself is a dictionary.
And, it can store the whole property list,
like set object for key, that first method you see there.
And, repeat, retrieve all property list,
or, it can just do little tiny pieces of property list,
like it has a method set double,
that will just set a double for a certain key.
Okay? So,
it's like a dictionary in it's key value, but
it only stores property list.
And, the big thing about the way it stores them,
is that it persists when your app is gone, okay?
So, you know,
normally, a dictionary is stored in the heap.
As soon as your app quits, it's gone.
But this, stays around, so
that when your app launches the next time,
all the data's still there.
So, that's why it's kind of like a database, but
it's very small, okay?
For preference and settings.
Here's how you use NSUserDefaults.
You use this type method in NSUserDefaults called
This gives you an instance of an NSUserDefaults.
You're always gonna use
the same instances like a shared instance.
You'll always to use the same one.
And then you just send it messages like, you know,
set object for keys, store this property list, and
get object for key to get it back.
Now, the changes you make in there,
are automatically saved, but, there is a method called
synchronized, which is kinda like save, which can force it,
to save out to disc.
Why do you ever need synchronized when it
auto saves?
Well, in auto save, when things happen like you app
stops being the front most app,
the user switched to another app.
So, your app is still there, but
it switched to the background.
It'll save then.
So, it won't always save when you want, and
especially when you're debugging, okay?
When you hit stop in the debugger,
it doesn't save, okay?
And so, you probably wanna throw some synchronizes in
there, to save this thing out,
especially when you're debugging.
It's not really that expensive to synchronize,
especially if there's not much data in there,
which there shouldn't be, because it's small.
So, it can't hurt too much to throw them in there.
And yeah, synchronize return to bool,
whether it failed or not.
But if it failed, there's really not much you can do.
So, a lot of times people don't even look at the return
values synchronize, they just synchronize, and they pray.
But, it's only not gonna save if like the disk is full or
something, that virtually never happens.
All right, so, I'm gonna do a little demo here.
And show you a little more about property list.
And also any object,
cuz you've never seen any object in action.
And what we're gonna do, is add a property to our
calculator brain, which gets its program.
So, our calculator brain has a program, especially your
versions, where you added variables to it.
It's got this kind of program,
it's almost like a little computer program.
You can execute that program, set its variables, it's great.
So, it would be nice if we could get that program.
But we don't wanna reveal our internal data structure, and
there's nothing anyone can do with that program.
So, we're really just getting it as a cookie.
And the only thing you can do with it, is give it back
to the calculator brain, or another calculator brain in
the future, and then say, evaluate.
Okay, so you see the value of having this?
So, basically getting the program out of
the calculator brain and send it back.
But we're gonna return it as a property list.
It's gonna be any object.
People are gonna look at this var, and
it's gonna be of type any object, and
all they can do is get it, and then set it back.
So, what would that look like?
Let's go over here to our calculator.
So, I've gone much bigger font here.
Hopefully everybody, is this big enough?
All right.
So, this is calculator as I last left it.
It doesn't have your enhancements to it for
the variables.
And I'm just gonna add a var here.
A public var.
Called program, and
it's gonna be type any object, but I'm gonna put comment here
that says guaranteed to be a property list.
So, the callers know that this is a property list.
So, for example, they could put it into NSUserDefaults.
Or they could write it out to disc.
I haven't taught you how to write it out to disc, but
you can do it.
So, they could do that with it,
cuz they know it's a property list, okay?
So, this is going to be a computed property.
Okay, so we're gonna have a get in the set.
And when the get,
we're just gonna get some sort of property
list representation of our internal data structure.
And when you set, we're gonna take that property list and
turn it back into our private representation.
So, the get,
how are we gonna represent ourselves as a property list
because this is really our internal data structure here,
it's array of op, op is an enum,
clearly enums are not NSDates, or NSStrings, or NSArrays.
So, we can't just return this thing, okay?
Cuz that's not a property list.
So, we need to return some sort of property list.
And I'm gonna return an array of strings.
And the string is either gonna be the symbol of an op,
in which case I will look it up in known ops, or
the string will be an numeric representation of
the string of the operand rather.
Right? The dub will
turn into a string.
Which I happened to have a var that does exactly that.
Okay? This turns an enum op,
into a string.
And I know how to turn it back.
Because I know how to use a number formatter to turn
the string of a operand back into a double and
obviously I know how to look up binary and
uniary operations in our known ops.
So, we could say something along these lines.
Our get, I'm gonna show you a much better get in a second,
but one way we could do our get is we could just say,
we could make a return value that is going to be
an array of string.
Okay, in fact let's just create an empty array of
string here, and then we can just through our ops,
in our opStack here, and for each op,
we can just say return value append, an ops description.
On to the end of it, and then we could return, return value.
Right, makes sense, I just my internal data structure into
a property list, because this is a property list.
Notice I can return, this through any object,
because arrays are bridged, and
as array which happens to be an object.
But this is an awful lot of code for this.
We can actually do this in one line of code, okay?
So if you paid attention in my last lecture, you'll remember
that I told you about this function called MAP.
[COUGH] And I can actually just return my opstack mapped,
so that each element description is returned.
So I don't need any of this, okay?
So everyone understand this line of code right here?
This is just a little closure that gets the opportunity to
convert every single thing inside op stack,
which is an op, these are ops, into a string and it takes
this string and maps them all and returns a new array.
So when you start getting familiar with Swift and
what it can do,
you'll start realizing a lot of things that take five or
six line of code, you can just do them in one.
Okay, but it does take some experience.
So now let's talk about going the other way.
So now we have, someone gives us back our program.
We wanna load our Ops dec back up.
Well the first thing we wanna make sure is that the thing
they're giving us back is an array of these symbols.
Because they could just give me junk,
because it's any object they could set it to anything.
So, I, I want to make sure that's what it is.
So what I'm going to do,
I'm going to let op symbols equal new value, right?
New value remember we are in the setter here.
So, new value is the new value which is in any object.
As an array of string, because that's what it has to be.
If this thing that they passed in is not an array of string,
I don't know what to do with it.
So really here what I wanna do is, if let as question mark,
because they could pass me anything.
And so, here I'm using the as to make sure this any object
they pass me is actually and array of strings, okay?
If it is, now I can convert back, so
I'm gonna create something called my new op stack which
is just gonna be an array of ops and I'm just gonna go for
each op symbol 1n this op symbols.
Op symbols is this thing right here, that's the array,
that's the new value, the any object converted.
Into an array of strings, okay?
So for each one if I can find a known op,
that matches that symbol then this must be an op,
and so I will just add that op
to, to my stack, otherwise I'm gonna see
if I could turn that string into an operand, and I'm gonna
do that with if let operand equal NS number formatter.
Okay, number from string, I'm gonna pass it
this op symbol that I got out of that array it gave me.
I'm going to use optional chaining here and
put a question mark, then double value, okay.
Remember optional chaining, if you have an expression like
this, this whole expression, and if anything along the way
returns nil then this whole expression will return nil.
If you put a question mark after that term.
So, number from string can return nil if this is
not a string, that can be interpreted as a number.
So by putting a question mark here it says, okay?
If that's nil than just return nil back to here.
Otherwise keep going and send it double value.
So this operand is either going to not get set,
because of the if let, or it's going to be a double,
which is what this double value comes out of here.
Okay, so that's kind of explaining from last week how
we did this NumberFormatter thing.
So in this case,
I can just append onto the end of it that a new op.
So, we'll say dot operand with that operand.
So we're just putting a new op on there.
So now I've built a new op stack using all those strings,
and I can just return that new op stack.
Not return it, sorry.
I'm gonna set my op stack equal to that new op
stack, okay?
That's it.
Any questions about that?
>> [INAUDIBLE] Property List?
Like that's just because.
>> This is documentation,
this is a public thing, it's not private, and I'm just
documenting to anyone who's using it that this is true.
I can document this even better using a typealias.
So you can basically have a typealias,
which is an alias for a type.
It's just a way of another type, and
I could have a typealias called PropertyList,
which is equals AnyObject.
Then I could have this type be PropertyList, okay?
So I just created PropertyList as an alias for AnyObject, so
someone looking at this is gonna go,
whoa, what the heck is PropertyList?
Now they're going to look at this typealias, and
say, oh, that's any object, but they're really going to
understand that I'm returning the property list to them, and
the reason it's property list, again, so
I can throw it again as user defaults or whatever, okay?
But otherwise, it's only good, the only thing it's good for
is giving it back to me to reset my program in
the future; that's all this thing is good for.
Now, you're gonna wanna add this to your assignment for
assignment three, okay?
So, all right, back to slides.
I'm not gonna, I don't have time to really run this
unfortunately due to in test code because we are.
Schedule constraint here.
So let's go back to here, continue.
All right, so now let's talk about, new thing here,
which is Views, okay, totally new topic.
A view, that is to say,
a subclass of the class UIView in iOS
represents a rectangular area on the screen.
That defines a coordinate space to draw in, and
also to handle touch events, okay?
That's what a view is, and you are going to learn today,
how to make your own views.
The views are hierarchical, so,
views are within views are within views.
Any view only has one superview.
Right, it's only contained directly in one view.
But, it can have any number of subviews.
So in our calculator, we had a top level view,
which I'm gonna talk about in a moment,
and then we had a lot of subviews, all the buttons,
the labels, those are all UIView subclasses.
Okay, so we made a whole bunch.
A bunch of subviews in that view.
But you can go even deeper.
It's hierarchical multi-layer.
We can go as deep as we want.
The two, you can see the two properties there for
getting your super view.
Notice it returns a optional, because you
can have a view that's not currently on screen.
It's not currently in the view hierarchy so that can be nil.
Your subviews, I show a ray of UIView there.
Actually the subviews property is an array of any object,
again for historical reasons, but
there will always be UIViews in there.
So don't even worry about that, okay?
And usually in this class on the the slides.
If something's an array of AnyObject,
if there's gonna be a certain kind of thing in there,
I'll say, array of the certain kind of thing.
But when you write your code, then,
obviously, you might see, oh, that's array of AnyObject, and
that's again, the bridging will work here,
automatically, so, no worries there UIWindow.
There is a certain thing called a UIWindow.
It's a subclass UIView but it has no meaning in iOS.
There is a UIWindow it's at the very,
very, very top of each device screen but
you never send it any messages really.
You don't interact with it.
It's all about views.
On a desktop windows matter.
But on iOS, it's all about views, okay?
So don't worry about UIwindow.
This hierarchy of views, subviews and
superviews, is created usually, graphically.
You can drag things out in the storyboard.
You can drag views inside other views to
create your hierarchy.
But you can do it in code as well.
It's pretty rare to do it in code, but you can.
There's two methods here addSubview and
removeFromSuperview, addSubview is sent to
the prospective superview.
In other words to the place you wanna add your,
this view into.
And removeFromSuperview is sent to the view that you
want to remove.
So these are sent to different,
okay, removeFromSuperview means remove yourself from
your superview.
So, where does the view hierarchy start?
Where is the kind of the root,
the top level of this hierarchy, and
the answer is very simple, but, for some reason,
students have a very difficult time understanding this one.
There is a property in your UIView Controller
called view, and
it is the top level view in the scene in the storyboard.
All right. So you have this story board.
You've got your scene that we've seen like your
calculator one.
The top level one the place where you're going to add
your top level views is called view in your view controller.
It's just a property in UIView Controller.
So it really couldn't be simpler, okay?
Sometimes students they just forget that's there and there
like all right how do I add something to my story board?
You know in code or something like that.
Well you just you add it to the view of
your UIView Controller, okay?
So, let's talk about initializing a view.
As usual, as I've kind of recommended, you probably want
to avoid doing an initializer, if you can.
So, to initialize any properties in
your view hopefully using equals or
some of the other ways we've talked about doing it.
But, in UIView it's slightly more likely that you
might want to have an initializer.
So it's a slightly complicated thing.
Only slightly.
In that UIView has two initializers you
have to deal with.
And that's because UIViews can be created in
two different ways.
In code by just saying UIView and
this frame initializer, which is just the frame in the super
view coordinate system where this view is gonna live.
But then there's also this init with coder.
And I'm not gonna talk about coders and
archiving and all that.
But that is the initializer that is called when the view
comes out of a storyboard.
So, if you create a view by dragging on a storyboard,
its initializer is this coder thing, okay?
So, if you want, if you absolutely have to use
an initializer to initialize something in your UIView,
you're gonna want to implement both of these things.
And I recommend this kind of format here.
Where you override init with frame there.
That is a designated initializer in UIView.
And then, there's actually a required initializer,
which is the decoder one.
So if you implement the init with frame you have to
implement the coder one, it's required,
cuz as soon as you implement the init with frame,
you'll no longer inherit all of your designated
initializers you won't inherit the init with the coder.
So, basically implement them both, and
then just factor your initialization code into some
setup method or something, and call that, okay?
So, that's only if you absolutely have to
have an initializer.
All right, there's another alternative to initializing.
Which is if you can wait until the initialization is finished
but you want something really early in the view's life,
you can implement this method awakeFromNib.
But that only works for
views that come out of a storyboard, okay?
awakeFromNib means I just awoke from coming out of
a snory, a storyboard.
So now you can initialize me.
So it's a little bit limited, because it only is good for
views that come out of a story board.
But it is an alternative.
Now okay, so
that's all I'm going to say about that actually.
Now let's go on to how do we.
Draw and handle events in this rectangular area.
Well we need a coordinate system to work in.
So there's some data structures I'm going to
show you real quick to get us started here, that we're
gonna use when we're drawing and handling events.
First of all, all coordinates are gonna be CGFloats.
Okay, CGFloats is a type in Swift struct.
And always use CGFloat when you're drawing or
handling events in a view.
Do not use double.
Do not use float.
If you use those in your homework,
you're gonna get dinged, okay?
Now doubles and
floats can be converted to a CGFloat using the initialized.
CGFloat has initializer for floats and doubles.
that you can pass and turn into a float.
But, you always want things to be into CGFloat when you're
working in the coordinate system, okay?
Now, there's a couple other structures that
are important here.
One is CG Point.
It's just two floats, X and Y.
Just defines a point in the coordinate system.
Couldn't be simpler.
Okay? And there's also CGsize,
just defines a width and
a height in the coordinate system.
Okay, so these are just simple structs.
That's all they are, structs.
I don't know, certainly Point,
I don't think has any other methods on it or anything.
Size, maybe, I don't think so, though.
But these two are combined, the points and
the size, to CGRect.
So CGRect is struct that has a point and a size.
The point is the origin of the rect.
Upper left, which I'm going to talk about in a second.
And then the size is the width and height.
Now rect has a lot of methods on it, okay?
Rect has things like minX,
which is the left edge of the rect.
Or midY, which is the midpoint of the rectangle vertically.
Or intersects.
And you pass another Rect, and it'll tell you,
boolean, whether it intersects that Rect, right?
The two Rects overlap.
Or, intercept, which says, take this other Rect and
create a smaller rectangle,
which is the intersection of those two Rects.
Or contains point.
Okay, we passed a point and
it'll tell you whether it contains that point.
So rect is this struct, but
it has a lot of cool methods in there.
A whole bunch more.
You can just type a rect, type and
then a dot Xcode show you all the cool methods it has, okay?
All right, so those are the types of data types we're
gonna use to talk about our coordinate system and
to write our code in coordinate system.
So let's talk about the Views coordinate system itself.
First of all, origin upper left.
Some of you might be used to origin lower left,
like Cartesian coordinates,
the origin is in the lower left.
But all this origin that you're using a view is in
the upper left.
So, that means increasing Y is down the screen,
increasing X is across.
So, I put an example, 500 dot 35, 500 comma 35, over there.
It's 500 across, and only 35 down, okay?
From the upper left.
The units that we're talking about here are points
not pixels.
Pick high, very high resolution displays have more
than one pixel per point.
Okay, 'cuz they are able to draw very find curves,
very fine text, et cetera.
You don't usually care
about the difference between points and pixels.
It's automatically handled for
you because you're doing things like drawing text or
drawing an arc and the system automatically uses as much
pixels as it can to make things nice.
But occasionally you do care,
because maybe you're drawing something very finely detailed
and you want to control pixel boundaries.
And if you want to do that you can call this,
get this property in view called content scale
factor and it will return the number of pixels per point.
So on a retina display, that's gonna be 2.
On a non-retina display, that will be 1.
It could be anything, though.
So you shouldn't write your code assuming it's 2 or 1.
You should just write your code, get the content scale
factor, and you'll know how many pixels per point.
99% of the time, you don't have to worry about this, but
in your assignment three, hint, hint, you might have to.
So what about the boundaries of where drawing happens.
You've got this rectangular area.
Well, there's a very important property called bounds,
which is a CGRect, which describes the area you're
drawing in in your own coordinate system.
Okay? It's important that it's in
your own coordinate system.
Because these views can be scaled and even rotated and
translated to other you know, coordinates systems.
But when you're using the bounds and
you're drawing it yourself, you have this rectangle and
you know where it is.
Now the origin of your bounds usually is 0,0.
But you could define it to mean something else, for
example, scroll views they define their origin to
be where they are in the thing they're scrolling,
that's just what they define origin to be, but
90% of the time, your origin is just going to be 0,0.
So, the width and
height is what you're going to care about,
how big a space am I drawing into.
There are two other properties that often get confused with
bounds, but don't let this happen to you.
One is center.
Center is the center of your view rectangle in your
superview's coordinate system, not in yours.
So if you write code that say, oh, I'm gonna start to
draw something in middle of my rectangle.
I'll get center.
That's wrong.
Okay? Center is in your super view.
Center positions you.
It has nothing to do with how you draw.
It just says where you are in your super view.
Same thing, frame, frame is a rectangle in your
super view's coordinate system that contains you.
Okay, it contains all of you, of your drawing.
But it's in your super user coordinates systems so
it's only good to position you, not for drawing.
When you're drawing, you use bounds.
To kind of give you a picture of this,
as better like you might think that
the frame size is exactly the same as the bound size.
Right, if it's a rectangle that contains my rectangle,
it must be exactly the same size.
But no, because view can be rotated.
Okay? And
if you rotate your view like this green view,
look how much bigger the frame is.
The rectangle that contains you is much bigger than you.
Because you're tilted.
All right?
Make sense?
So don't confuse frame and center, which are about
positioning, with bounce which is your size and
area that you're drawing into in your own coordinate system.
Okay, so creating views.
How do we create views?
Most often views are created by dragging them out to
the storyboard.
That's 95% of the time.
And af, but
of course, your views aren't sitting in the object pallet,
there's buttons and sliders, but there's no your view.
So you have to drag it out of generic view.
It's kind of at the bottom of the pallet and
you just drag it out.
Once you drag it out though,
you have to go to the identity inspector and change the class
of that view that you dragged out, so that it's your class.
Okay? So it's a two step process.
You drag a view, your custom view out and then you change
this class to view whatever your custom class is on rare
occasion, you might create an instance of your view in code.
Probably never in this class,
maybe in your final project, although I doubt it.
You just do that using UIView's frame initializer.
Right? So you just say
view equals UIView frame.
You can even do UIView with no arguments and
it'll give you a CGRect to zero which is a frame zero
size and width and
then you could later set its frame to position the view.
So here's an example of creating a view in code.
I'm going to use UILabel.
UILabel is a subclass of UIView so I can send it.
UI labels parentheses frame.
So I just give it this frame which is kind of in
the upper left.
Notice I've created this view but it's not on screen yet.
Assuming this code here is in a view controller,
I could then say view.addSubview.
Cuz remember view is a property in
my view controller which is that top level view.
And I could say add subview of the label and
boom, it'll appear on screen.
Okay? At 20,
20, width 100, height 50.
So it is possible to create views in code.
We almost never do that in iOS.
We use storyboards.
Okay, so when do I wanna create my own UIView?
Well, when don't you want to?
Well you don't want to if you want a button or a slider or
anything where there's already an iOS view.
But if you want to do something that
those things can't do, draw something special or
handle touch events like swipes and
taps and pinches, differently or for a different purpose.
Then those things then you create your own custom view.
So, we're going to focus on drawing today, and
I'll do gestures in the next lec, in the next lecture.
To draw in your UIView,
all you need to do is override this method drawRect.
Okay, very simple method only takes one argument.
In the code, it's actually called Rect, but
I've renamed it to be regionThatNeedsToBeDrawn, so
that people understand what that argument is.
That argument is purely an optimization.
It's just the system saying, hey, draw yourself and really,
I only care about this rectangle of you.
But you can draw your, the whole self if you want.
But this is the only rectangle I really care about.
So if you had a View that knew how to draw parts of itself,
okay, then it could only draw the part that's in
this rectangle, but
you can completely ignore this rectangle if you want.
It's purely an optimization.
This is very important to understand,
you never call drawRect, ever, never.
If you call drawRect in your homework,
you're going to be a major ding.
Because I'm standing here in front of
you with red text on my slide.
Saying, never.
Do not call drawRect, drawRect is called by the system.
It's called by the system when your view needs to be redrawn.
How does the system know your needs,
view needs to be redrawn?
Because somebody called setNeedsDisplay on your view.
And you can call it on yourself if you want.
If something has changed about your view, some property or
something that would change the way it's drawn,
you call set needs display on yourself,
eventually the system will call your drawRect.
But it's got to set some things up first, okay.
There's other views around you.
That it has to get organized.
Okay, so never call drawRect, drawRect it's called for you.
When you call set in use display, you can specify I
really only need this rectangle redisplayed and
then obviously,
that rectangle's gonna show up in the argument to drawRect.
Okay, but if multiple people call set in use display with
different rectangles, probably the union of those
rectangles might get called here one time for drawRect.
Make sense?
All right.
So, how do you implement this, pow,
all-powerful drawRect method?
You can use, kind of a C-like, function-based API,
called Core Graphics.
That's what it's all based on.
Or you can use a nice object-oriented, thing,
called UIBezierPath, which is what we're gonna use,
because we like object-oriented.
But since the underlying stuff core graphics is what
UIBezierPath is built on, I'm gonna kinda explain a little
bit about the concepts of Core Graphics.
How one draws in Core Graphics.
The first thing in Core Graphics that's important is,
you have to get a context to draw in.
Okay? So
if you're printing you'll have a printing context.
If you're drawing on screen you'll have a draw on
screen context, et cetera.
And inside drawRect, when you're drawRect is called by
the system it sets up the drawing context for you.
So it's automatically set up,
and it's called the current context.
The current drawing context.
And you can get it if you were using the CAPI.
Or C-like APIs it's still swift but it's functional.
Function API, you can get it
with this function UIGraphicsGetCurrentContext.
And it will return this cookie,
it's just this magic cookie.
You don't know what it is, but it is the context of drawing.
Because all of the C-like functional API take
a context as an argument.
Okay, BezierPath doesn't do that no worries,
but that one does.
So the first thing you need is a context automatically set up
for your when drawRect is called.
Then you create paths, you create paths with lines and
arcs and things like that.
Then you set the drawing attributes,
like the colors you want to draw in, the line widths, and
things like that, and
finally, you stroke and fill those paths.
And you can do either, or, or both stroking and filling.
So that is how, that is fundamentally how you draw.
Things like drawing text is just the fonts know how to
have the perfect path to draw beautiful letters.
And so, your stroking would outline the letters, and
filling fills in the letters, okay?
So even drawing text, is just stroking and filling paths.
Okay? Now, UIBzierPath, this object
only thing on top, it always draws in the current context,
you don't have to worry about context.
It has methods, to do things like line two, arch two,
to build the path.
It also has methods for
setting a lot of these attributes like line width and
things like that and it has methods to stroke and fill.
So all those core graphics are put into one class.
Let's look at UIBezierPath, and
how we would use it to draw a triangle, let's say.
First we create a path, so we just say, UIBezierPath.
There are other initializers for UIBezierPath but
you could also just create an empty path, and
then you can start moving around.
So, here I'm going to assume this screen is 160 points wide
by 250 wide, and I'm gonna draw, this triangle here.
So I'm gonna move to 80 which is halfway across and
50 which is a little bit down,
and then I'm going to add a line down to 140, 150.
Then I wanna another line over to 10, 150.
Then I'm going to close this path.
So that's it, that's how you can make a triangle.
Now, I'm kind of lying to you a little bit, because as you
execute these, nothing is appearing on screen.
The screen will be blank.
Okay, I was only showing them so you could understand what I
was doing there with the lines.
So it'd be blank.
To make it appear, you have to stroke or fill.
So, let's set the attributes we want.
So, I'm gonna set the color for filling to be green, and
the color for stroking to be red.
Okay, now notice that I set those with methods on UIColor,
not with methods on the BézierPath.
Okay, that's a little interesting but
that's the way that works.
But line width, I do step by talking to the paths, so
I sent a message to the BezierPath,
set line with the three.
So once I set up all my attributes, now I can fill,
for example by saying, path fill and
see, I get the rectangle filled in green.
Or, I can say stroke and it'll stroke the red line.
Pretty straightforward?
Most of learning to draw is all about
understanding UIBezierPath and all the methods on it.
Question back there?
>> if I have two shapes onscreen,
how would I set the colors be different for each one?
>> If you have two what?
>> If you have two different shapes onscreen?
>> Oh, yes. So, the question,
if I have two shapes onscreen,
how would I set the colors be different for each one?
I would set the colors, stroke or fill one.
Set the colors on something else, stroke or
fill the other.
>> If the path isn't closed, what does fill do?
>> So the question is,
if the path is not closed, what does fill do?
Well, it depends on, I think the winding rule, and,
other things about how it draws.
But, it will try to fill it in.
Okay. I mean a path could
cross back over itself.
Right? And so filling it
might be hard to understand.
And so there are rules for that.
I'll talk about that just briefly in just a second, but
it will fill.
But there are other things, if you don't close a path then
you can't do things like hit detection, right?
Detecting whether a point is inside it
because it's not closed, so there's no inside.
So you can, UIBezierPath also has kind of
pre-canned cool shapes like roundedRect, okay,
which is a rectangle that the corners are rounded off of, or
obviously ovals and circles, it has ways of doing that.
You can also clip to any arbitrary path.
So clipping,
hopefully everyone knows what that means.
That means, if I set it up, if I have a path, and I say clip.
If I say, Add Clip, then all drawing I do after that
will only show up if it's inside that path, okay?
So, for example,
if you were drawing playing card on screen,
playing cards have little rounded rects, so you might
draw your playing card in a big rectangle, and then clip
to a rounded rect, so that the corners get [INAUDIBLE], okay.
That's a simple to get a rounded rect playing card.
You can also do hit detection as I mentioned, the hit
detection and the filling depends on the winding rules,
and there's this usesEvenOddFillRule, property,
which is only yes or no to say which winding rule it uses.
And so people who don't know what that means,
don't worry about it.
In fact, don't worry about hit detection or
any of this stuff,
cuz you're not going to have to do this for your homework.
But I just want you to know that BezierPath has a lot of
other stuff besides just linetwo, linetwo,
fill and stroke.
Okay, it has other stuff.
And when you want to look at UIBezierPath,
check out its documentation and
kind of experience it in its full glory.
Okay, let's talk a little bit more about colors because I
used a color there, that green color.
UIColor is just an object.
It's really powerful.
Colors not only can be
standard colors like UIColor.greenColor, but
you can create a color from RGB, red, green, blue, or
HSB, that's hue, saturation, and brightness.
You can even set your color to be a pattern, so that when you
draw with that color, it actually draws some image.
So that's kinda cool.
Views have a background color, which you can set on them.
Also colors can be transparent.
They can be see-through, partially see-through.
This is called alpha.
So alpha is a value between zero and one.
Zero means fully transparent, so
you can see completely through it, and
it won't even show up on the screen.
To one, which is fully opaque.
It obscures anything behind it.
Anything drawn with this color.
And you can create a transparent color from
another color with this color with alpha component.
This would create kind of half transparent yellow.
Color to draw with, and you can draw in your view with
these transparent colors, okay?
Using these BazierPaths.
However, if you draw in your view with transparency,
you must tell the system that you are going to do that.
And you do that by setting this property on
your UIView called opaque to false.
And why is this required?
Well, because drawing with transparency is expensive.
Because the views behind you
are gonna start showing through.
Okay? So the system has to
blend basically the different views that are overlapping.
That's expensive in terms of graphics power.
And so it doesn't wanna do that if it doesn't have to.
If it can just draw the bit.
If you don't set this to false and
you try drawing transparently, it's not gonna work.
Cuz it's not gonna blend with the things behind.
You can make your entire view transparent.
It has its own property alpha,
which you can set to anywhere between zero and one.
It'll make your view,
everything you draw in the view will be
however much transparent you want it to be.
What happens when views overlap and have transparency?
They're blended as I said,
remember that the subview's order matters.
Okay? The thing that is first in
the subviews array is in the back.
And everything down the rye is in front.
So, when the transparency happens,
that's the order it's gonna happen in.
You can completely hide a view by sending it hidden equal to
true, and this is surprisingly common to just hide a view,
and maybe it appears when a certain condition is true on
screen, or whatever you can set up this view in
your storyboard and then set it to hidden, to true.
And so it comes up, it's not there.
And then in code you say hidden equals false, boom,
it appears, okay?
So that's a way to, completely hide the view.
A hidden view not only doesn't draw,
it doesn't receive any touch events either.
But it's still in the view hierarchy.
It's kinda there waiting.
All right, drawing text.
So, usually you draw text on screen with a UILabel.
That's what you did with your display, et cetera.
But you might wanna draw in your drawRect and
you do that with this class called NSAttributedString,
which I'm really not gonna cover.
You wanna look at
its documentation if you wanna draw a text.
I'm not gonna do a demo of NSAttributedString, but
I'm gonna give you some code for
your assignment next week, and I'm
gonna use NSAttributedString in the code I'm giving you, so
you'll be able to see an example of how it's used.
But basically NSAttributedString is
a bunch of characters and
some attributes on those characters.
Now attributed string has a couple of interesting things
about it.
It's easy to use.
You can just create it, pass it a string, and
then say draw at point, and that will draw the string with
its point, its upper left corner at that point.
And you can even find out how big it's gonna be by asking it
the size of this attribute string.
So it's pretty easy to use in that way.
Mutability is not done with var and
let for NSAttributedString.
There's a different class
called NSMutableAttributedString.
Okay? And why would you
want a mutable string attributed string?
Well because these strings,
what makes an attributed string attributed,
is you can put attributes on the characters in this string.
And so a mutable attributed string,
you can set the attributes.
You can get the string out as an NSString,
using the String property on NSAttributedString but
NSAttributedString itself is not a string.
It's not like a subclass of NSString or anything.
It's its own thing.
So you set
these attributes with functions like setAttributes.
It takes a dictionary of attributes.
I'll talk to you about what the keys and
values are in a second.
And then arrange.
That's an NSRange right there by the way, not a Swift Range.
And the indexing into these strings is
the old style NSString version.
It's not the string.index stuff we
talked about last time.
It's literally which Unicode character are you
talking about setting an attribute at?
So, this dictionary has a bunch of attributes.
The attributes can be things like,
set the foreground color of this text.
Set the stroke width.
Okay? Because remember,
text is just paths.
You can stroke them and to set the width or the font,
is a really important one to be able to say.
Set this text to have this particular font, okay?
And so you just set a dictionary that has
the key NSFontAttribute and the value would be a UIFont.
Now let's talk about UI fonts, briefly.
Fonts in iOS 7 and later, including iOS 8,
are very important to get right, okay?
Look at these screenshots.
Look how important the fonts are.
Okay? They're critical,
to making the UI look good.
So, you've got to get the right fonts in your app.
And where do you get the right fonts?
Well, the number one place to get a font,
the right place 99% of the time, is this class function
in UI font called preferred font for textile, okay?
The text style is something like headline, or
a body, okay?
The body would be like the main content that your user is
looking at.
Headline might be like a title at the top.
Footnote might be a small little thing mentioned at
the bottom, okay?
And there's a whole bunch more of these textiles.
And so, you're gonna ask
the assistant give me the preferred font for that style.
The font includes a size.
You want font to include the size, as well as the face.
And users can go into their settings on their phone and
change and say I can't see it anymore.
Kinda like me.
And they make their fonts bigger.
And so this would
automatically get that new size.
Okay? So that's why you
always wanna use preferred font any
time you're displaying any user content, okay?
Now, it is possible to create fonts where you say,
give me arial 15 narrow or what.
It is possible, but you really shouldn't be doing that.
You should be using this preferred font for style.
There's also a system font.
This is something that appears on buttons and
in things like segmented controls, things like that.
You never wanna use only the system fonts for user content.
User content you wanna use the preferred fonts.
So yeah, other ways to get fonts, ignore them.
I'm not gonna talk about it, okay?
All right, how about drawing images?
How do you do that?
Well there is a UI label for
images, it's called UI Image View.
And you can use that to put an image on screen, but
you might wanna draw an image, in your drawrect as well.
And usually you create an image by name, from that
Images.xcassets thing that we moved out of the way real
quick right at the beginning of our very first demo.
And if you click on that, it's pretty self-explanatory.
You can drag images.
You can have different images of different resolutions for
different devices.
It's really pretty powerful.
That's usually create an image.
By name and dragging into the images.xcassets.
You can create it in other ways from the file system,
which we haven't talked about, or even with drawing, but
we're not gonna talk about that either.
Once you have the UIImage, you can draw it by just sending it
the message, draw at point.
And it will draw in its normal size with that point as
it's upper left.
You can also say drawInRect, and
it'll scale to fit that rect, either bigger or smaller, or
you can draw a pattern in rect it'll tile it repeatedly to
fill the rect.
So really easy to draw images inside your draw rect.
And the last thing I wanna talk about is,
what happens when your bounds changes in your UI view?
Like the, the user rotates the phone.
Now your bounds are wide and
not very tall, instead of tall and not so wide.
You might think,
oh my drawRect's gonna get called, and I'm gonna redraw.
But no, that does not happen.
Instead, the bits that were on screen get stretched.
So, usually that's not what you want.
You can and
result in a very grainy, kind of bit stretching of your UI.
But that is the default,
is the bit stretching because it's higher performance.
Now, you control though, you can control what happens to
your view when the bounds change,
with this property in view called content mode.
And here are some of the content modes you can set.
There's left, right, top, bottom, et cetera.
All these ones basically don't change the bits.
They don't re-draw, but
they just move your entire view to the left edge,
right edge, top, or bottom of the new bounds.
These are kinda strange ones.
You probably don't need that very often.
Then there's the scaling of the bits.
So here's the default, ScaleToFill.
'Kay, we'll scale the bits to fill the new bounds, but
you can also fill or fit the bounds and
keep it aspect ratio.
So that won't stretch your bits.
It'll still scale them, but
it'll scale them with your aspect ratio maintained, or
you can have the wonderous redraw, 'kay?
This is the one you wanna set that says,
when my bounds change, call my drawRect again.
With the new bounds.
So you're gonna want that quite often.
I'll show you that in the demo today.
All right, so let's get right to this demo.
Today, it's gonna be a little tight to do this in 20 minutes
but, cuz we're gonna cover a lot of stuff but
I think we can do it.
We're going to start by creating a new project.
So I'm just going to file a new project,
this is brand new.
Okay? We're going away from
the calculator here.
And as always we're gonna be
using the single view application thing right here.
I'm gonna call this project Happiness,
because we're gonna draw a face, a smiley face.
Okay? And it's gonna be
able to smile or frown, depending on how happy it is.
So that's why we're gonna call this Happiness.
I'm gonna put it in the same place I put my calculator,
which is my home directory developer there.
Here it is.
As before I'm gonna move this AppDelegate out of the way.
I'm gonna move, actually let's take a look at image.assets.
So here's the appicon slots.
You can see that for any given image,
like the appicon, there's lots of different resolutions,
depending on the devices that you can provide.
You don't have to provide all the different resolutions,
it will interpolate but you can do it.
So you just drag an image into here, and now you can get it
by name with that UIImage named function.
So we'll get that out of there.
We don't want the launch screen either.
So here we are.
I'm gonna show you something fun here too.
Here is my storyboard.
I'm gonna show you how to add a new view controller to
your storyboard.
And in fact, I'm gonna delete this one.
So, you see this one right here I've selected?
I'm just deleting it.
It's gone. I'm gonna delete its
code as well.
I'm gonna go here, and just delete, move it to the trash.
Now I have a storyboard with nothing in it, and
I've no view controllers.
So, how do I add a view controller?
You go over here to your object pallet.
The very first thing is a view controller.
And you just drag it out, and boom.
You've got one.
Now, the class.
What class is this going to be though?
This is gonna be Generic UIView controller, so
it's pretty useless.
Okay? You can't add any outlets, or
have any actions because it's not a subclass of UIView
controller, so you can't put any of that implementation in.
So, we're gonna create a new subclass of UIView controller.
So let me show you how to do that.
You say New > File.
And last time we went to New > File,
we said Swift file to make the calculator brain.
But now we're gonna go over here to Cocoa Touch Class,
which we do anytime we wanna create a subclass of
an iOS class.
And here I'm creating a subclass of UIView controller.
I need this.
So I'm gonna click on that.
Here's where I pick what I want it to be a subclass of.
You can see there's a lot of things in here.
I'm gonna say I want a view controller.
It wants to call it view controller by default.
We don't even like that for
the calculator, let alone this.
So I'm gonna call this my happiness view controller.
So it creates that.
Put it in the same place all my other Swift files are.
Here it is.
Happiness view controller.
Again, I'm gonna get rid of this code.
Just to make things a little clearer.
So here's my happiness view controller.
It's just like the view controller you had,
that was called view controller in your calculator.
It was called Happiness view controller.
But this guy right here,
is still playing old view controller.
How do I know that?
Well, it's this magic right here.
You see this identity inspector?
Okay. You can see the class of
this selected thing here, in this UIView controller.
And I don't want it to be UIView controller.
I want it to be HappinessView controller.
So, very important to understand that in
storyboards, everything is linked up to
your code by name.
Only by the name.
That is the only thing that links what's in
your storyboard with your code.
The names of outlets, the names of methods,
the action methods.
The name of your UIView controllers.
The name of your UIViews.
Okay? It's all by name.
So you have to go to the identity inspector here and
make sure that name matches up.
So now, you can see
this says that it's a HappinessView controller.
So that's making me happy.
Now, we want to put our custom view in here, okay?
So how do I add a custom view to my UI?
And the answer is, I go into the object pallet.
It's all the way at the bottom,
near the bottom somewhere.
Where is that thing?
There it is.
You can drag out a generic view.
So this is just a UI view.
Now when I drag it out,
I'm gonna put it right here with the dashed blue lines.
'Kay, because we know that that's a good thing to do.
And I want to set up the auto layout so
that this view always fully fills my scene here.
Okay? Now I could control drag to
the edges like I did before, but since this is already
perfectly where I want it, and I use the blue lines, I
can go down here and use Reset to Suggested Constraints.
Now, I'm only showing you dribs and drabs of autolayout.
I was gonna talk all about autolayout today, but
we need to move on to this.
But I will be having the lecture on auto layout,
probably week after next.
In the meantime I'm just gonna show you little bits of it and
you can explore.
I'm never gonna ask you to do a ton of it in your homework.
So this one I'm gonna reset to suggested constraints, and
it added those constraints.
And I could go over here.
Let me make some more space here.
I could go look at them over here.
You've all determined that.
Another place you can see the constraints of a view is
here in the size inspector.
If you look towards the bottom of the size inspector-
Look, here's all the constraints, see?
Trailing, leading, top, bottom.
It added all these because these are the suggested
constraints based on the dash blue lines that I used.
So, oftentimes if you put something in place perfectly
with the blue lines,
you can use recess-suggested constraints.
We coulda done that with our display in our calculator.
We couldn't do it with the buttons.
Cuz it just wasn't lined up enough with other things, but
we could have done it with the display.
All right? So now we have this.
Now, this UIView, let's look at its identity in Spector.
It's just a generic UIView.
That's no good.
We need a custom UIView sub-class.
So let's go do that.
File > New File.
Same thing,
I'm gonna create a subclass of a coco touch class.
This one's not gonna be a view controller,
it's gonna be a UIView.
Okay? I'm gonna call it face view,
cuz that's what it's gonna do.
It's gonna draw a face, okay?
Put it in the same place, put everything.
Here's our, my face view right here.
We'll get back to that in a second.
In our storyboard,
we wanna make sure that we inspect our Face View.
And change its class to Face View.
By the way, it can kinda get tricky sometimes,
picking the right thing.
Like, sometimes you're picking the UIView controller,
sometimes you're picking the view.
One kinda cool trick, CTRL+ Shift.
If you hold CTRL+Shift and click, then it
lets you pick which thing you want, that's under the mouse.
A very important little trick, CTRL+Shift.
So here I could pick the view controller.
Now I'm seeing the identity inspector of
the view controller.
Or I could go here and pick,
this is the view property in my view controller.
The top level view.
This is my FaceView that I'm trying to create.
So I select this here, then I'm gonna go to
the Identity Inspector again, up here.
I'm gonna change this to be a FaceView.
So now this thing is a FaceView, and so
when it's time for this thing to draw,
it's gonna call the Draw Rect of the FaceView class.
So let's go take a look at the FaceView class.
Here it is.
And here is drawRect.
Now, it's commented out by default.
Why is it commented out by default?
Well because having a drawRect costs system resources.
It thinks it has to draw this thing.
So you only wanna have a drawRect if you actually draw,
in your drawRect.
Don't have it just sitting there uncommented out,
if you're not actually using it.
All right, so, how am I gonna draw this face?
Let's start by drawing the outline of the head.
Just a round circle.
Cuz that's pretty simple to draw.
And how am I gonna do that?
Let's start by creating a BezierPath.
I'll call it facePath which is, BezierPath.
And let's look at
all the different kinds of BezierPaths we can make.
We could use this one, ovalInRect, okay?
That's a pretty good one.
I'm actually gonna use this one,
arcCenter cuz I wanna show you a really bad error message
you're gonna get in swift that I want you to avoid.
So I'm gonna use this one which lets you create an arc,
and I'm just gonna have my arc go all the way around.
Okay? From zero all the way around.
So, it wants to know the center.
So I'm gonna have to have my faces center somehow.
We'll define that in a second.
It also wants to know the radius, so
I have to have my faces radius in a second.
We'll do that.
It wants to know the start angle, to draw this arc.
Okay? So
I'm gonna start at angle zero.
The angle's in radians.
So I wanna go around to 2pi.
Going, I'm gonna draw an arc from zero,
all the way around to 2pi radians.
Hopefully everyone knows what radians are.
So zero to two pi that's good, and then clockwise or
counter clockwise,
I don't care cuz I'm going all the way around.
So I'll just say clockwise true.
Now, when I do this, well first let's do
our faceCenter and faceRadius here.
So I'm gonna make actually computed properties for those.
So the face center is a CG point.
And really, it would be cool, yeah, I could do get and set.
It's gonna be Get Only.
I'm not gonna allow me to set my face center,
I'm almost going to calculate it.
I could make it settable, actually too, but
I'm gonna make it so it's calculated only.
When you do calculated only,
you actually don't need this part.
This get.
You can take that out.
And if it's Return Only.
So I'll say return.
Now I, you might be saying oh I'm gonna return center,
let's put the face in the center.
Did everyone understand why this is very bad?
All right, cuz center is not in my coordinate system.
It's in my super views coordinate system.
Luckily, I can convert a point from my super views coordinate
system using this nice method, convertPoint from view.
So I'm gonna convert center from my superview.
That's gonna put my face right in the center,
cuz I've converted that center from my superview's coordinate
system to mine.
So that's faceCenter.
Let's also do faceRadius.
So for the faceRadius,
I'm gonna have it be as big as it can be.
So faceRadius is gonna be a CG flow to all of
our drawing stuff is flowed, and I'm gonna return as big as
it can be without getting cut off.
So it's gonna be the minimum of my width or height.
So I'm gonna say, min of my bounds.
Bounds.size.width, or my bounds.size.height.
Whichever is less than that.
Min is just a nice little function in Swift.
And I'm gonna divide by two because this is a radius,
not diameter of my face.
This is the radius.
So now I have my center facing radius, but
I still have an error here.
Look at this error.
Even though I did my center, I did my radius.
I still have an error.
What is the problem?
Oh, extra argument radius in call.
That's weird.
I didn't see any extra argument.
What the heck is that about?
You could waste a half an hour looking for this, okay?
I was thinking, oh,
radius, there's nothing wrong with the radius.
It's right.
The problem for this has nothing to do with the radius.
The problem is this.
So this is an error you get from switching occasionally,
I'm sure they'll fix all these errors, but occasionally,
you get this very misleading error, and this is one.
The problem is that this-
What type is this?
It's a double.
And what type do we draw with?
So this problem is just that I need to cast this to
a CGFloat.
That's gonna make this error go away.
It had nothing to do with radius whatsoever there.
So, best warning, watch out for that.
All right, so I have my nice facePath here.
Maybe I wanna set my facePath's linewidth.
Something like three, something like that.
In fact, maybe I even want linewidth to
be something that's configurable in my FaceView.
So it's a CG float.
And we'll set it to default of three.
One thing that's interesting and
then we'll set this down here to line width.
One thing that's interesting about this is any time
someone changes my line width, I need to redraw myself.
All right?
So how can I make that happen?
Well, I can use Property Observer.
Anytime someone sets this,
I'm gonna call setNeedsDisplay on myself.
I'm in it redrawn by the system.
Okay? So anytime you have
a property in your, in a view that changes how you would be
drawn, just put a little property observer into
the setNeedsDisplay after it.
And it'll get you automatically redrawn.
Maybe I'll have another property here,
which is the color I wanna be.
UIColor, we'll set the default here to be
blueColor for example.
But I want the exact same thing here.
Someone sets it.
I want it redrawn.
And so let's go ahead and set our color.
Now I'm gonna do both my set, I could do set fill and
set stroke colors, but I'm just gonna do set,
which sets both my fill and my stroke.
I'm not gonna be filling so it doesn't matter, but set to
both, and then how do I draw this BezierPath right here?
I just say, facepath.stroke.
And that's gonna draw, okay?
So let's go take a look at this.
So, it comes up, it's blank.
And not only is it blank, it's got some error down here.
What does it say? It says,
failed to instantiate the default view controller.
Okay? Well that means that in my
storyboard, there is no scene that's specified,
as the scene to start in.
Okay? It has no starting scene.
The starting scene always has a little arrow pointing to it.
Right here,
you've probably seen in in your calculator right?
A little arrow that says this is the place to come in
when you start this app.
You can set that arrow with the inspector the regular
attributes inspector by selecting your
view controller that you wanna be the one that comes in.
And click. This little button here is
initial view controller.
You see that arrow appear?
You can also pick this arrow up and
move it to another view controller.
So now we have a thing, oh all right.
So we've got our face.
Now, one thing I don't like about this face is it goes all
the way to the edges.
I want it to actually be 90% of the edges.
Okay? Another thing I
don't like is this.
Ugh, okay?
So my face is gonna be distorted every time I rotate.
Why is that?
That's this stretching thing I was telling you about.
We want our draw rect to be called again when we
have a new bounds like that.
So let's fix both of those things.
We can fix that content mode redraw by selecting our view,
going to the inspector up here,
the attributes inspector, and
the very first one is the mode.
And you can see the default was scale to fill.
We want redraw.
Okay? So I'm gonna pick redraw.
So that'll fix that.
And as far as the 90%, I'm going to fix that in here,
by having the, my face radius be times 90%.
But really, I'd like this to be settable too.
So I'm gonna have var scale, CGFloat.
Okay. And we'll start this out at
So I want that, and
this one also, if I change, I want it to change.
So now let's go try that.
See what happens.
Okay? We got our 90%.
That's good.
And when I rotate, it doesn't stretch it.
Okay? And it's using the minimum of
the width or height.
So that's good.
That's going good.
Now we wanna draw the eyes and the mouth, and
cuz time is running out here,
I'm actually gonna type those in super fast.
I posted for those of you who are trying to keep along here,
which is probably impossible.
To do that though, I need some constants.
And this how we do constants in Swift.
We create structs.
Okay? And then use class, or
in this case, static lets.
Okay? So this is gonna be
a constant called Scaling.FaceRadiustoEyeRadius.
Okay. And
then I'm going to have a function here, that I'm
gonna type in really, really fast, called bezierPathForEye.
And it's just gonna return a BezierPath that draws an eye.
Either the left eye or the right eye.
This says which one.
Okay? So it returns a BezierPath.
Then I'm gonna have another one, which is BezierSmile.
We're gonna type that in real fast.
So here is BeizerPath for smile.
And what this is gonna do,
it's gonna take a fraction of the maximum smile.
So one is full smile.
Minus one is full frown.
And everywhere in between.
'Kay? So that's what this does.
And it returns a BeizerPath.
So now here in my drawRect,
I can say bezierPathForEye, left eye.
I got the BezierPath.
I'm just gonna immediately send it stroke.
And then BezierPath for the right eye.
I'm gonna send it stroke.
So I'm just gonna stroke the left eye and the right eye.
Then I'm gonna do the smiliness.
So, I'm going to let smiliness equal some value.
Let's start it at 0.75.
We're pretty, pretty happy to start.
And then I'm going to create a bez,
this BezierPath using this thing up here for
a smile, which is bezierPathForSmile.
And we're gonna pass it the smiliness.
And then I'm just going to stroke this smile path.
And I hoped to have a little more time so
I could have gone through this code, but
it's a pretty straightforward code, really.
Here we're just using add curve to point in
our BezierPath.
To draw a curve, which is the curve of the mouth.
And then here,
we're just using BezierPath arc center again.
To create the circle.
The eyes are just circles, okay?
That's really all those two methods do.
There's nothing really special about them.
And what do I have error here, which is, oops.
That's not it either.
SmilePath oh, equals, not that.
There we go.
So let's run this.
Hopefully, we'll have a smiley face.
There it is.
He looks pretty happy.
We can rotate him.
He's very happy.
Okay, let's make him a little sad.
How about minus 0.5?
Minus 0.5.
Pretty, pretty sad.
So now we have this nice FaceView.
What we're gonna do in our next lecture, is we're going
to add a gesture so we can make him happy or sad.
Happy or sad.
We're also gonna put a gesture in there, a pinch gesture so
we can make them bigger or smaller.
Then we're going to actually add a whole other MVC and
make a much more powerful application that just uses
the FaceView.
The happiness view controller as a view in another MVC.
So now we're going to do multiple NBCs.
So that's what we're gonna be doing on Monday.
    You must  Log in  to get the function.
Tip: Click on the article or the word in the subtitle to get translation quickly!


Stanford - Developing iOS 8 Apps with Swift - 5. Objective C Compatibility, Property List, Views

3199 Folder Collection
MARURU published on May 24, 2015
More Recommended Videos
  1. 1. Search word

    Select word on the caption to look it up in the dictionary!

  2. 2. Repeat single sentence

    Repeat the same sentence to enhance listening ability

  3. 3. Shortcut


  4. 4. Close caption

    Close the English caption

  5. 5. Embed

    Embed the video to your blog

  6. 6. Unfold

    Hide right panel

  1. Listening Quiz

    Listening Quiz!

  1. Click to open your notebook

  1. UrbanDictionary 俚語字典整合查詢。一般字典查詢不到你滿意的解譯,不妨使用「俚語字典」,或許會讓你有滿意的答案喔