[ Music ]
>> Stanford University.
>> Well welcome everyone
to lecture number seven
of CS1973P Fall of 2013-14.
Today we are going to talk
about input and output.
Okay? It's about views which
you're rectangular areas
on the screen that you can
use both to draw custom stuff
and to get gestures in --
from user touch gestures,
multi-touch.
I'm going to have a demo that's
going to show you all that.
We're going to build
a custom view,
it's going to have its
own custom gestures
and all that stuff.
It's basically going
to draw the --
you know, the cards that we used
in Machismo were just
really bad like A clubs.
They didn't look like
cards so we're going
to actually have a custom
view that looks like a card.
Right? Has the things in
the corner, and face cards,
all that -- that whole business.
All right, so a view is of
critical importance in iOS.
Its right at the heart
of all the drawing we do.
You've used a ton
of views already.
Buttons are views.
Labels are views.
Okay? It's basically
the building block
that represents a
rectangular area on screen.
It defines a coordinate space.
Okay? A coordinate
space you can draw in.
And a coordinate space that
you can get touch events in
and understand where they are.
It's hierarchical.
Right? So you can have
view inside views,
inside views, inside views.
All right?
Every view only has
one super view.
But a given view could
have many sub views
and those sub view
are just rectangles.
They can overlap, whatever.
You know they're not required
to somehow be tiled or separate.
They're complete free form
sub views of a given view.
You can have any number of sub
view you want for a given view.
The order of the sub
views does matter
because they can be transparent.
We'll talk about that.
And one thing that's sometimes
a little confusing is you have
this rectangular area
that you're view,
but you can actually
draw outside that area.
Perfectly legal.
There is a little switch
you can turn on in xcode
or obviously a property
in UI view
that says "clips my sub views."
In other words don't let my
sub views go outside my --
my views bound.
Right? So kind of keep
things contained if you want.
But general that's not the
default and generally we don't.
There is a UI window class.
Very unimportant in iOS.
If we're on the Mac,
UI window would matter.
You got a map, windows
on a desktop.
But in iOS it's all about views.
There's only one UI window.
It's the one that's
containing all the views
that are currently on screen.
That's why in the last
demo I was able to say
if self.view.window, then
I knew I was on screen.
Right? Because that's the only
window there is and so if I am
in it then I'm on screen.
So UI window very unimportant --
important you don't
even aver had to look
at the documentation
for UI window.
UI views are what
it's all about.
So this hierarchy of views.
View inside views
is most often built
in xcode just by
dragging views in.
Now we haven't done a lot
of dragging views inside other
views but it's perfectly legal
to do that and you'll
see in xcode sometimes
when you drag it try to
drop it in another view.
You don't want it
to but it tries to.
So a lot of it would
build graphically.
However, we could also build
it in code and when we build it
in code, build and tear down
this new hierarchy and code
with these two methods,
add sub view.
Exactly what you would think.
It just takes a view and adds it
as a sub view of
some other view.
You send that to the
view that you want
to be the parent view right.
That's the view to which
you are adding the sub view.
On the opposite side,
to get view out of the view
hierarchies you send remove
from super view to the
view you want to remove.
Not to it's parent.
So you don't say remove
from super view:view.
You just say to a view
that you want out of there,
remove yourself from super view.
Okay, so it's a little
different adding and removing
when you ask to do it.
You're NVC's of course
have a view.
And that top level view
that contains all the views
in the view hierarchy for
your NVC is the property view
in view controller.
So if you look in UI view
controller there's a property
called view.
It's the UI view.
And if you go in xcode and
right click on your controller
or right click on the back round
of your NVC's view you'll
see an outlet basically.
Because it's really is an
outlet, this view property.
And it points to
that top level view.
So that's a good place
to start if you want
to start adding views in
code to your view hierarchy.
And of course when you drag them
out in xcode that's
what you're doing.
You're dropping them in.
Unless you're dropping
them in on top
of another view you're dropping
into that background,
the self.view.
Okay self.view.
We use that phrase
self.view but it's self.view.
Self.view is that top level
UI view in UI view controller.
It's just a plain UI view.
It's not a subclass version.
You never subclass that view.
It's just kind of a
big container view.
And that's another thing
to know about view is
that you don't always subclass
them to draw or do touch events.
Sometimes they're
just boundaries.
Right? Just a view you want
to find a coordinate space
and you want to put some
other views inside of it.
Perfectly fine, we do that.
So initializing the UI view.
A little more common to want
to override the designated
initializer
of a UI view in a subclass.
Remember in UI view
controller we kind of said,
we almost never do that because
UI view controllers are almost
always coming out
of story boards.
So the designated initializer
never gets called in anyway.
So we just do awake from NIB.
Okay? And we never do that
with NIB name bundle thing.
That's not true in UI view.
In UI view you do more
often need to do something
in your initializer and
just want to, you know,
have your initializer
around and code in there.
So but when you do it, you
want to also do awake from NIB.
Okay? That's because UI
views are equally created --
well not equally but at least
they're commonly created both
by dragging them
into a story board.
So that would be the awake
from NIB initialization
and by sending [inaudible]
to them.
In other words in your
code creating a view.
So for example, in your
homework you're going to have
to do set cards and playing
cards for real, not as buttons
but actually drawing them
and you will almost certainly
be allocating those views
because there can be -- there's
not a fixed number of them
so you can't really
drag them out in xcode
because they have
to come and go.
So you'll be doing that.
So we do the same kind of thing
we did with UI view controller.
Right? Where we have some
kind of setup method.
Setup being whatever you want.
And then you call it
from wake from NIB
and then you also call it from
UI views designated initializer
which is called in
knit with frame.
And that frame specifies
where this view is
in its super views
coordinate system.
It's the positioning
of this view.
Okay? So that's what the
code would look like.
Look like that.
Put your initialization
codes up.
And we'll do that in the
demo just do you see that.
Now before I can talk more
about UI view and drawing there
and getting events we got
to define a few types here.
One is CG float.
It's a floating point number.
All floating point numbers
that have to do with drawing
on the screen or
getting touch events
or whatever are CG floats.
This might be a double.
It might be just a regular
floating point number.
It might be 32 bits, 64 bits.
You don't know and
you don't care
but you always have
to use GC float.
Not only using GC float to
specify positions on screen
but you know, I think if
you're going to be multiplying
or adding numbers to
things that are on screen
to recalculate new
things, all --
you want to do all that
in the GC float domain.
So you're going to have
a lot of properties
in local variables
that are GC floats.
When you're doing screen stuff.
Okay? Then there's a
c-struct called GC point.
That has just got
two elements in it.
X and Y. That's an
X and Y position.
Okay? In the drawing world.
And there's GC size which
is just a struct with width
and height which are
both GC floats also.
And that's just specifying
a width and height.
And then there's GC
rect which is c-struct
with those other two
GC structs in them.
GC point and GC size.
That specifies an
origin and a width
and height for a rectangle.
Okay? So you've got
to know these four.
I'm going to refer these
left, right and center.
Whenever we're drawing on
screen this is what we're
talking about.
Okay? The origin of a views
coordinate system for drawing
or for handling events
is upper left.
Now lower left.
Not like Cartesian coordinates.
Okay? This is -- you --
drawing from the upper left.
So positive Y values
are down the screen.
Okay? So you can see I put
that point up there 400,35.
That's X and Y. X is 400 way
over to the right and 35 is Y
and positive so it's down from
that origin on the upper left.
Okay? The units in all this
drawing are points, not pixels.
Okay? Probably you already
understand why this is
because some devices
have lots of pixels.
Right? I mean it's very
high density pixeled
like these retina displays.
Other ones like some of
the iPads, older iPhones,
they don't have as many --
they're not as dense of pixels.
Well if -- if we
didn't use points,
if we used pixels then things
we drew would be really small
if the pixels were
small and really big
if the pixels were big.
So we abstract that
away by using points.
Okay? How many pixels
per point are
on the display that
your view is in.
You can find out by using
this UI view property content
scale factor.
So it returned two for
example on a retina display
because there are two pixels
per point on a retina display.
One on a non-retina
iPad for example.
You know probably not going
to need to use that property
in this class, this quarter.
Previous quarter we played
around with it but
not this quarter.
But more importantly there
are these properties that talk
about the position and extent
of your drawing system.
Okay?
It's really important to
understand the difference
between these properties.
First you have this GC
rect property bounds.
That is the origin and width
and height of the drawing area
in your own coordinate system.
Your own coordinate system.
A system you are drawing in
and handling touch events in.
Okay? GC rect frame
at the bottom there,
that is a rectangle that
completely contains you
in your super views
coordinate system.
So you can see how
that positions you.
Right? Because that's in your
super views coordinate system.
That's where you are.
Okay? Center is just the
center of where you are
in your super views
coordinate system.
There's no property
to get your center
in your own coordinate system.
You just take your balanced
out width divide it by two
and height divide it by two and
-- boom you're in the middle.
Okay? Now you might think
frame and bounds are going
to be exactly the same except
for that the frame is going
to be in the super
views coordinate system
so the origins might
be different.
But that's not true.
Okay? And the reason for that
is that views can be rotated.
Okay? And if a view is rotated
you can see that the rectangles
in containment might
be much bigger.
Right? Because it's a diamond
shape that it has to contain.
Okay? So there's details
on here in this slide.
You can look at them
at your leisure
but bottom line is understand
that frame is a rectangle
containing you
and your super views coordinate
systems bounds is the rectangle
you use to draw when
you're drawing
in your own code in your view.
It's your coordinate system.
Yeah?
>> When would the origins
of bounds not be zero zero?
>> When did the origins of
bounds not be zero zero?
That's an excellent question.
Origin of the bounds since it's
your own coordinate system is
up to your own interpretation.
So for example scroll view
uses the origin to say
where am I looking in the
thing I'm scrolling on?
That's just the way
it defines origin.
You can define what you
want origin to mean.
Okay? Because you're
going to be asked to draw
and you know your own
coordinate systems.
You're going to draw
whatever you're asked to draw.
So it's kind of up to you.
99% of the time your
origin is zero zero.
Because it doesn't
need anything for you.
It's just the width
and height that matter.
But that's a very good question.
Okay? So again, you can look at
the slide is a bit more detail
but frame and bounds is for
making sure you understand that.
So most often you create
views by dragging them
out of the pallet in xcode and
that's when you're like dragging
out labels, and buttons, and
scroll views, and text views.
Those are all views.
So you just create
them by dragging out
and you could even drag
out your own custom views.
But the way you do that is
you drag out a generic view,
and then go to the identity
inspector and change its class.
Exactly like how we drag
out a view controller.
We have to go change its class
to be one of our custom classes.
Right? Because xcode obviously
doesn't know our custom classes.
At least doesn't
know in the kind
of runtime way that's happening
in the story board so we have
to set the things -- it
works exactly the same
as setting view controllers.
Creating a view in code,
in other words not dragging
it in to a story board.
Just you [inaudible] with
frame or [inaudible].
It you do [inaudible], that's
the same as [inaudible]
with frame GC rect zero, which
is a rectangle with origin zero,
width and height of zero.
Okay? So you can do either one.
If you do [inaudible] presumably
you better set the frame
to something so that
it knows where to be
when it's put into
its super view.
So here's an example of
creating a UI label in code.
You see I said UI label
in knit with frame.
I gave it a -- a rectangle.
That's in the super view system.
And then I add sub view that
label to self.view which is
that top level view
in my view controller.
And so it ended up at 20/20,
20 X and 20 Y, and its 50 wide
and 30 high in my NVC view.
Right? Now you probably wouldn't
ever create a label like this
because you drag them out.
But you might create your
own custom view like this.
Right? Okay, so when do I want
to create my own custom views?
Obviously, when I want
to draw something custom.
Not something that a button
or a label can draw for me.
Or when I want to handle
special touch events.
Swipes, or pinches, or
something like that.
So the drawing side of this
which we'll talka bout
first is really easy.
There's one method in
UI view called draw rect
and all you have to do
is implement that method
to draw what you want.
Okay? One method, that's it.
Implement this method
to draw what you want.
Now it has that argument there,
that's a performance
optimization.
That's basically a
rectangle that says well,
please draw yourself but really
I only need the stuff that's
in the rectangle in
your coordinate system.
But if you want to ignore
this rectangle and draw all
over yourself, that's
fine by me.
Okay? So it's purely
performance thing.
Some views it'll make sense
to look at that rectangle
and be really efficient.
Like we had an assignment a
couple years ago in this class
where you're drawing a graph.
Well if you're drawing a
graph and you're having
to calculate every point it's
kind of nice to only have
to calculate the points in
some part of the graph instead
of drawing the whole
thing every time.
But if you're drawing a playing
card, it's so light weight
to draw it so you can just
draw all the parts of it.
You don't have to
use the rectangle.
>> If you draw outside
of the rectangle does
that stuff show up?
>> The question is if you draw
outside the rectangle does
that show up?
Remember that everything
you draw shows
up unless you have one of
these clipping things I talked
about on, where your
super view is clipping you
or you have a clip rect
as part of what you draw.
So the answer is that rectangle
has nothing to do with clipping.
Okay? No matter what
that rectangle is,
clipping or not is unaffected.
They have purely performance --
giving you a hint of
what needs to be redrawn.
That's all it is.
So really important,
never call draw rect.
If you called draw rect
in a homework assignment
in this class you are
going to get dinged.
Because I'm putting it
in red -- never red.
And I'm telling you don't
ever call draw rect.
Draw rect is for
the system to call.
Okay? If you want your view
redrawn you call the method set
needs display.
That tells the system this
view needs to be redrawn,
and then the system
will call draw rect
at an appropriate time.
Okay? The system knows when
it's an appropriate time
to call draw rect,
update off screen buffers,
whatever the heck it has to do.
It's in control of that.
Don't ever call draw rect.
It just will not work
to call draw rect.
Okay? Set needs display.
Then you can do set
needs display in rect,
giving the little
optimization rect.
Okay? But set needs
display is how you do that.
Okay? All right so, how do I
implement this draw rect thing?
Okay, I got this
draw rect method.
I want to draw something.
Well the answer is you use this
library the course library's
called Core Graphics.
Core Graphics.
It has a ton of C
functions that all start
with GC, Core Graphics.
They almost all take in
context as a the first argument.
We'll talk about that.
Or you can use this nice class
called the UI [inaudible] path.
UI [inaudible] path let's you
build all these complicated
shapes, into a big path,
and then you can stroke
it or fill it on screen.
Okay, so let's look into these.
To understand these we need
to understand a little bit
about how Core Graphics
thinks and it thinks
in the following
four step process.
You got to have a
concept to draw in.
You got to create paths;
triangle, squares, whatever,
rounded rect, whatever
it might be.
Then you set the colors and
the fonts you want to use,
and line widths and
all that stuff.
And then you stroke or fill
the paths that you created.
This is the fundamental
way Core Graphics goes.
Okay? So let's talk
about all those things.
UI [inaudible] path by the
way encapsulates all --
doing all of that.
It takes care of
the context for you.
So you don't even have
to think about that.
You create paths by sending
messages to the UA --
to a UI [inaudible]
path instance.
It let's you set colors, and
line widths, and all that
and then it has methods
to stroke and fill.
So it encapsulates all that.
So let's talk about the context.
Mostly you don't have
to worry about context
because you're going to
use UI [inaudible] path.
I'll show you a way if
you're using the GC functions
to get the context
to draw on screen.
But a context means where
am I drawing in terms of --
am I drawing on screen
right now?
Am I drawing off
screen, in some bitmap?
Am I creating a PDF file
out of what I'm drawing?
Am I drawing to a printer?
iOS has great printing
support so you could be drawing
to print a page on an Air Print
printer or something like that.
So that's the context
part of it.
For normal drawing UI
kit sets this concept
up for you before
it calls draw rect.
And then once you're in draw
rect context is ready to go.
UI [inaudible] path knows
what it is, and if you want
to call the GC functions you
call this method UI graphics get
current context.
If you call this inside your
draw rect you'll get a little
cookie that you can hand
off as the first argument
to all these GC, you know,
Core Graphic functions.
Okay? And you're not going
to need those GC
functions very much.
Only when UI [inaudible]
path won't do what you want,
which is pretty rare.
Okay? So this is a magic thing.
That context ref is
it's just a void star.
You don't know what it is.
It's opaque.
And this thing is new every
time your draw rect is called,
to never keep that thing around.
It's only good from the start
of your draw rect to the end.
Don't keep it in a property or
anything that's going to live.
Okay? Call the UI graphics
to get current contents each
time you start a new product.
Okay, so how do we
define a pass.
So let's say we wanted
to do a triangle
with UI [inaudible] path.
We [inaudible] UI
[inaudible] path like this.
We move to where our
starting point is.
So I'm going to move
to the top there.
75 across and 10 down.
Then I'm going to add a line
to that point going down to
over to 160 down to 150.
I'll add another line
to come back to 10, 150.
And then I'm going to close this
path, by calling close path --
that goes back to where we --
draws a line where we started.
So I have a nice triangle there.
Now I'm kind of misleading you
because as I'm making all those
calls in UI [inaudible] path,
nothing is actually happening.
Okay? The screen is blank.
Okay? Because all I'm doing
here is building that path up,
that UI [inaudible] path.
I haven't actually draw it yet.
When I want to actually draw
it I have to set my fill color
and stroke color and you
can do that by calling,
sending set fill and set
stroke to a UI color.
Get a UI color like you did for
attributed string, whatever.
Same thing.
The call set fill or set stroke.
You can even just say set
and it'll set the fill
and the stroke to whatever
color you're set to do.
And once you have
your color for fill
and stroke set you send a
message to the path saying fill
and or message to the
path saying stroke.
And now it will actually draw.
So those last two calls
there, fill and stroke.
Those are the things that
cause drawing to happen.
Okay? Everything else
is just like setup.
Make sense?
Okay. Now this might all seem
like whoa, triangle great
and I can draw a triangle.
Sounds easy.
But there's actually a lot
the UI [inaudible] path can do
that's really much more
sophisticated than that.
You can set your line
widths and things like that
to make your drawing, you know,
into patterns and all kinds
of that to make them
more interesting.
And also UI [inaudible] path
has a lot of cool functions
like [inaudible] path with
rounded rect corner radius.
Okay? And that will
give you a path,
which is a rounded rectangle
when it's inside of
a certain bounds.
Okay? And it has a bunch of
other ones similar to that.
So that you can build
more complicated things
than line to line to line to.
Okay? I just show you
that because it's simple.
Right? So that's how you would
for example create an oval.
You can also use UI [inaudible]
path to flip your drawing.
This is super important.
You will need this for
your homework I think.
I supposed there's a way to
do the homework without this
but I can't think of a way.
Well I can but it would
be extremely tedious.
[Laughter] Rounded rect
for example could be used
to clip your drawing.
So if you wanted to draw
some kind of pattern
but you wanted it to be inside
a rounded rect you just get a
rounded rect, just like earlier
in the slide there
and say add clip.
And at that point
-- from that point
on all your drawing will
be clipped to be inside
that [inaudible] path.
Okay? And there's ways
to add more clipping on,
to turn off your clipping,
you know, that kind of thing.
So clipping important piece
of UI [inaudible] path.
Okay let's talk about drawing
with transparency UI view.
Okay, UI views by
default are opaque.
They have a background
color, by default it's white.
So if you put a view on
screen and just run it'll come
out as a white rectangle.
Okay? So that's not
always what you want.
So for example in a playing card
I want it to have rounded rects
and I want those
corners to show through.
Maybe there's a card behind or
something on the playing table
that the card is
on or something.
So I don't want it to be opaque.
So you're going to see in
our demo we're going to have
to turn this opaqueness off.
The way you turn the
opaqueness off is you have
to set this property
opaque in UI view to no.
In other words you have to
tell the system this view is
not opaque.
Even if you set the
background color to nil.
Okay? Which means I don't
want a background color.
It still won't be
transparent because of this.
This opaque isn't a
performance optimization
but it's a hard optimization
meaning it just won't work
if you don't set it
to the right thing.
So if you want your view
to be transparent you
have to set opaque to no.
You're also going to want to
set your background color to nil
so that it's not filling
with a background color.
It is also possible to make
your entire view transparent
with alpha.
Okay? So UI view has a
property called alpha.
If you set it to
50% then everything
in your view will
be 50% see through.
Right? 50% transparent.
20% or whatever percent
you want.
That's the entire view.
No matter what's
going on inside.
And of course you have
transparent colors
that you could draw with, fill
with, stroke with, whatever.
To draw transparently
that way as well.
So there's a lot of ways to
draw transparently in your view.
What happens if you've draw
transparently and views overlap?
Well they show through
to each other.
Okay? And I told
you that the order
in which you add the
sub views matters.
Okay? Every view has a property,
an NS array called sub views.
It's the list of views
and the order matters.
The lower one in the array,
okay, the ones that --
like zero are in the
back and the ones
down at the end are
in the front.
All right?
So the sub view array
is from back to front.
Okay? And things can
overlap and [inaudible]
and they will show through.
If you have an opaque on
in the middle, it would all
of a sudden block all
the ones in the back.
See what I mean?
So it's as simple as that.
Now having transparent
views is not cheap.
We talk about performance
optimization,
one of the biggest mistakes I
think computer science students
make is premature optimization.
You're in there optimizing
stuff that just doesn't matter.
This matters.
If you have a lot of
transparency it's going
to be a performance hit because
you're talking about having
to composite those view on
top of each other with alpha.
Way more expensive than just
blasting the bits in there.
Okay? Anyone taking graphics
knows that I'm talking about.
So this is something
you're going
to be a little careful with.
You're going to set
that opaque to no.
Understand there's a
performance cost there.
[Inaudible].
You can also hide
a view entirely.
Okay? So it's just like
think of it as alpha of zero,
like you can't see anything.
But it also won't
get any gestures.
It's almost like removing
from the view hierarchy
but you're leaving it so it has
its space in the sub views list.
It gets to keep that but
otherwise it doesn't appear.
It doesn't draw, it
doesn't get events.
It's like it's not even there.
Why do you want that?
Well because sometimes
you just want
to temporarily remove a
view from the hierarchy
and put it right back in.
So you said -- or put it back
in some short time later.
So you say hidden equals yes.
It's gone.
You hit hidden equals
no, oh it reappears.
Okay? So that's better than
setting the transparency.
So I think mostly UI kit
probably optimizes alpha equals
zero to be the same as
[inaudible] is my guess.
Okay. Probably won't have to do
that in homework is my guess.
But you might.
Depends on what kind
of sophisticated
[inaudible] you build.
I'm not going to go through
this slide but you can imagine
that if you're setting up fill
colors and clipping and all
that stuff in a sub routine,
when you [laughter] come back
from the sub routine all those
things might still be set.
And that would be bad.
Okay? So you want to be able
to push and pop your state.
Basically save the
concurrent graphics context
and then restore it.
And that what this functions
do, GC context save GC state,
GC context restore GC
set [inaudible] state.
We'll save everything.
All the fill colors
and clipping regions.
Go do a bunch of stuff.
You know, clip and fill and
everything and then you restore
and you're back the
way you were.
So that if you have
your draw rect
and it calls this draw green
circle thing it won't come back
to your draw rect and now
everything is drawn in green.
Okay? So that's a push
and pop [inaudible].
All right, drawing text.
Okay, it's all great
drawing triangles
and filling them with colors.
What if you want to draw
text inside your view?
Well of course we use UI label
to draw the vast majority
of text on the screen in UI kit.
But you can use NS
[inaudible] string
to draw any text you want.
And it could not be simpler.
You create the attributes string
with all the attributes
you want.
No restrictions, exactly
like you learned to do
in this last homework assignment
and then you just send a message
to that attributed
string, draw at point.
This point.
And that will be the upper
left corner of a rectangle
that includes all
that attributed text.
Okay? It's -- could not
be any simpler [laughter].
You can also find out how big
that text is going to take.
How much space that thing is
going to take by sending size
to the attributed string and
it'll tell you how big it is.
Now you might be a little
disturbed by this and like whoa,
attributed string is not
really a UI kit thing.
All the properties
inside usually are
but it itself is not.
And that's true.
UI kit actually adds to these
methods draw point and size
and there's a few other ones.
It adds them even though it's
in a completely different
framework using a mechanism
in objective C where you
can add methods to classes
without sub classing them.
Okay? All the categories,
talk about Wild West.
That's why I don't show you
until the [laughter] halfway
through because it can be
abused, this mechanism.
But that's how this
is working for those
of you who are wondering.
Drawing images is
almost the same.
But instead of an NS attributed
string you get a UI image.
So you know how to get one
out of the image assest
library with image name.
There are other ways you
can create the files.
You can even draw into an off
screen bit master UI created
UI image.
Once you have a UI image in
your hand you just send it,
draw at point, which
will draw that image
with its upper left hand corner
at the point you specified.
Or draw in rect which will
scale the image to fit
in the rectangle you specify.
Okay? This is a scaling one.
Or draw as pattern in rect
which will like tile the image.
You know, repeat the image
to fill the rectangle
that you specified.
Okay? And you can get
other representations,
PNG representations of
things you've drawn an all
these things.
So I just wanted to let
you know that's in there.
You don't need to know that
for your homework but anyway.
So the drawing text and images
really easy is just attributed
string and UI image draw
at point, draw in rect.
Okay? What about when
your bounds change?
Okay? You notice when you
did the navigation controller
or the tab bar, you know, you're
bounds of your view got shrunk
down to fit the thing
at the bottom
or fit the thing at the top.
And also if you rotate your
device obviously the bounds are
going to change.
What happens when
your bounds change?
Well by default the bits
of your view, the --
that were last draw
will get stretched.
Okay? Which really is
almost never what you want.
Okay? I mean most
content wants to bed drawn
to get high resolution
to look nice.
So the default is that because
it's way high performance then
asking you to draw again.
But there's a property
in UI view.
You really want to know
called content mode.
And it says what happens
when your bounds change.
And the one down at the bottom
there UI view content mode
redraw is doing what you
imagine, which is it asks you
to redraw your --
your whole view
with draw rect whenever
your bounds change.
Okay? So in our demo we're
going to set it to redraw
because if our cards bounds
ever change we want to redraw
so we get a nice
sharp face card image
or the right side little pips
on there and text and all that.
So you might want
to do that as well.
Okay, UI view content mode
scale to fill is the default.
That's the bit stretcher.
Okay, so that's the
drawing side of things.
Now let's talk about
the input side.
Okay, the recognizing gestures.
It is possible to
get the raw data
about fingers touching
the screen.
How many you're touching, where
the fingers are, when they move.
But -- and it used to be
[laughter] that you actually had
to look at all that
data to figure
out what the heck the user was
doing with swiping or pinching.
It was a nightmare.
I mean I can't believe
people actually did it.
It was quite esoteric code.
But the last few iOS releases
have had the right way to do it
which is gesture recognizers.
Okay? So the way we're going
to understand what the user's
doing is this system is going
to recognize certain
gestures for us and tell us
when those gestures
are happening.
Okay? Obviously you
want to do this.
The class that is the base
for all these recognizing
gestures is called UI
gesture recognizer.
It's an abstract class.
Okay, you never instantiate it.
But it has a lot of
concrete clips of classes
like pinch gesture,
tap gesture, all that.
And those are the things that
does the actual recognition
of a gesture and communicating
with you when it does.
There's two parts to suing
a gesture recognizer.
You have to create a gesture
recognizer and add it to a view.
You can only add a
recognizer to a view.
Because views are the only ones
that have a coordinate system
to know where the touches were
that cause the gesture
to happen.
And then number two is you've
got to provide the handler,
a method to call when
the gesture happens.
What is happening?
If it's like a pinch
it's, you know,
that gesture handle is
going to get called a lot
as the pinch goes out and in.
Or if it's a pan.
Okay? But if it's a swipe
or a tap you're just
going to get called once.
Make sense.
So those are the two
things that you need to do.
Usually number one is
done by controller,
although it's sometimes
views do it to themselves
where they add a gesture
recognizer to themselves.
And number two is often
provided by the view.
In other words the
handler, the thing to do
when the gesture is
happening or happened a lot
of times the view
implements that method.
So even though the controller
might add the gesture recognizer
to the view what it adds
is, it tells the system oh
and when you recognize the
gesture let the view handle it.
But the controller
can handle it itself.
So we'll do both in the demo.
We'll have the controller
handle it.
We'll have the view handle it.
The whole deal.
So let's take a look
at the code.
What the code would look like
to add a gesture recognizer
to a view.
A gesture recognizer to
a view in a controller.
So this code that I'm trying
right here is in a controller.
It's the setter of an outlet
for a view called panable view.
Okay? That's the name of
the outlet, panable view.
So this would just control drag.
Write it up and now as an outlet
and now we're in the setter.
And in the setter what
we're going to do is
when that view is set in
our controller we're going
to add a gesture
recognizer to that view.
So that that view starts
recognizing the pan gesture.
Pan is put your finger
down and move around.
That's called panning.
So it's very simple.
You can see that we
create a concrete sub class
of gesture recognizer called
UI pan gesture recognizer.
You see that there
in the yellow bubble.
And when we create it you can
see it's designated initializer.
All the concrete classes
designate initializer take
two arguments.
One is the target.
That's who's going to handle
this gesture when it happens.
And the second thing is the
method to call in that object.
So here pan is going to be
sent to the view itself.
The same view that we're going
to add this gesture
recognizer to.
And then we just do the step
of adding the gesture
recognizer to the view.
The method add gesture
recognizer is only implemented
by UI view.
So a controller can't
recognize a gesture.
It was no coordinate system
to do that in any way.
So some view has to be
handling this gesture
and yes you can have
self.view recognize gestures.
Right? The whole view.
That's perfectly fine.
And yes, the target in action
could be the controller.
A method in the controller.
It doesn't have to be a view.
Okay? That can be any object.
So how do we implement that
target, that pan calling.
So I'm panning, I'm pinching
and I'm getting sent pan
calling when I'm panning.
What do I do inside that method?
And the answer is that
each concrete subclass
of UI gesture recognizer
will provide some methods
to help you.
Okay? So for example the pan
gesture recognizer provides
these three methods.
Translation and view, that's
how far the touch moved
since it's -- since
basically it was last reset.
Okay? I starts off reset
when the touch goes down.
So as you move unless
you reset it
which you can do
in your recognizer.
We'll show you that.
It's going to tell you the
cumulative translation distance
from where you started.
Okay? So that's translation
view.
Philosophy in view is
how fast it's moving.
Is your finger moving
quickly like it's almost
like a swipe or something?
Or is it moving really slow?
Is the user trying to
really do some detail work?
So this is telling the velocity.
And then set translation
view allows you
to reset the translation.
Okay? Because I told you
translation [inaudible] is
since the last reset.
Set translation will let you
to reset it at some point.
And then the next
pan call in you get,
the next finger movement
the translation will be
from the place you just reset
it to, to the last time.
Okay? So -- so those are given
to you by pan gesture recognizer
and you also inherit --
an gesture also inherits
a very important property
from UI gesture recognizer.
The abstract super
class called state.
Now state can be a lot
of different things.
Here' some of the
more interesting ones.
UI gesture recognizer state
began means this is a continuous
motion kind of thing like a pan
or a pinch and it just started.
Finger just went down.
Okay? Then UI gesture recognizer
state changed means it's one
of those continuous
things and it changed.
The fingers moved.
Okay? Chance means it moved.
And then there's UI gesture
recognizer state ended.
Finger went up.
Okay? Exactly what
you would think.
But there's other states like
UI gesture recognizer state --
what's it called -- recognized.
Okay? That one you get
for discrete gestures
like taps or swipes.
You don't get all this began,
moved or began change ended.
You just get recognized.
I recognize the swipe.
I recognize the tap.
See? The different state there.
So recognize you'll never
get on the continuous ones.
Well you might get it but you
wouldn't pay much attention
to it.
But on the discrete ones
that's what you look at,
vice versa with the
began change [inaudible].
You can also get
cancelled and failed.
Things like that.
These happen when things
like you're in the middle
of a gesture and a
phone call comes in.
Okay? Boom, that gesture just
got blown out of the water.
So really good code will pay
attention to failed and canceled
and make sure these things
aren't in a wacky state
if the thing gets aborted
right in the middle.
So I'm not going to show that in
the demo because time is going
to be of the essence here.
But you can look at the
documentation on how
to handle [inaudible].
All right.
So what would pan
look like given
that we have those methods
the concrete things provides?
So I'm probably not
going to do anything
when the finger first goes
down because nothing
has really changed yet.
But as soon as it
starts changing
or when the finger goes
back up I'm then going
to update something
inside my view based
on where this new position is.
And I'm going to get
this new position
by asking the recognizer,
the pan gesture recognizer
what's the translation
in the view self.
Okay? The reason
we specify the view
when we say the translation is
because we need a
coordinate system.
Okay? We got to know the
coordinate system we're talking
about is translation
B. So self in this case
because pan call
in is in the view.
Like I might set my
origin or some property.
This is just an example line
of code if I had a graph
of something like that.
Maybe be moving the
graph around.
It's just constantly
resetting the origin.
And here since I'm moving the
origin from where it exists
at the time this is called by
some little translation I want
to reset that translation
every time.
So that I'm always getting
little incremental translation
movements and I'm moving my
origin around incrementally.
Otherwise I'd have to keep track
of where did my origin start?
And then every time
this was called I'd have
to find the difference and
then move it [inaudible].
This way I don't have
to worry about it.
I just keep resetting
the thing back to zero
and so I keep getting
these tiny changes.
I just keep applying
them to my view.
This very common pattern to do
this for these continuous ones.
Constantly resetting.
We'll do that in our demo.
We're going to do pinch but
we'll do the same reset.
Okay, so let's look at some of
the other concrete recognizers.
Pinch; okay, pinch doesn't have
translation it has a scale.
So when the pinch first
starts the scale's one, 1.0.
And as I go out 1.1,
1.2, 1.5, 2.0.
1.5, 1.2, 1.0, .9, .8, .7, .6.
Okay?
Okay? That make sense?
So, that's what's
happening there.
And then, the velocity is how
fast, in scale factor change,
per second, that's happening.
So, are you pinching really fast
in, pinching fast out, etcetera?
So, that's giving you,
again, some indication,
what's the user -- if they're
pinching out really quickly,
repeatedly, maybe they're
trying to really zoom out fast,
you can zoom out faster
than usual or something.
Rotation Gesture Recognizer,
also a two-finger thing,
like a pinch, except for
you turn your fingers.
And it's going to tell you
the rotation in radians.
Everyone know what
radians are, right?
Zero to 2 pi around
a circle, in radians.
Look it up if you don't know it.
And that's also telling you
velocity in radians per second,
how fast they're --
the person's rotating.
Swipe Gesture is one of
these discrete gestures.
You just set up this Swipe
Gesture, you alloc an init
with the target in the action,
and then you set these
properties' direction
or number of touches required.
Number of touches
means this is a swipe
with two fingers or one finger.
That's what number of touches
means, two finger, one finger,
three finger, five
fingers, whatever you want,
and the direction is left
to right, right to left,
top to bottom, or bottom to top.
Okay? So, you create it,
and then if a swipe happens
that meets all that,
you'll get the --
your handler called once with
"recognized" as the state.
That make sense?
So, swipe is something
you don't track;
it just tells you,
"Oh, I saw a swipe."
And, yes, it's smart about --
if you have a Pan
Gesture Recognizer
and a Swipe Gesture Recognizer;
if you swipe smoothly enough,
it will see it as a swipe.
But if you go slower, or
you don't go, you know --
you're going in a diagonal
or something like that,
it will say, "Oh,
no, that's a pan."
Okay, so it knows how to kind
of tell the difference
between gestures.
Okay? As much as possible.
It's pretty cool.
Tap Gesture, just
like Swipe Gesture.
It's discrete.
You set it up, how many taps?
Double tap, single tap,
how many fingers, etcetera.
And it will send you
"recognized" when it sees it.
>> Can you do Swipe Gesture
Recognize [inaudible] four
corners, or no?
Is it only those
four directions?
>> The question is, can
you do Swipe Gestures --
like, you mean, like swiping up
into a corner, as opposed to....
>> Right [inaudible].
>> I don't think so.
I think it's just
left-right, up and down.
>> [Inaudible].
>> I could be wrong about that.
I haven't looked in the
documentation for a while,
but I think it's
only those four.
All right, so let's do the demo.
Okay? So, it's going to be --
I'm going to go fast
through this; there's a lot
to show you here, okay?
But I'm only going to show you
all the things we just talked
about today.
But I'm going to try and
show all of it to you
in a comprehensive demo.
Let me talk about
what's coming up so
that we can just finish
the demo and be done.
Again, we're hoping on Friday
to get this university
developer program thing going,
but it looks like
that's still not working.
Watch Piazza tomorrow for
whether Friday is canceled.
At this point, it's
not looking good.
That would be a Stanford-only
thing, anyway.
The homework, as I said,
is due a week from Monday.
So, more than seven days
from now [laughter],
a week from Monday.
I really strongly recommend
you get started on the part
that is a custom view and
gesture recognizer immediately.
Do not wait until next
Tuesday or Wednesday,
because then you're going to
find that this is a lot of work,
for this assignment, to
try and jam into one week.
That's why I've given
you, you know, whatever --
however many days it is.
Do this part -- I would try and
do this part before next Monday
if you can, but certainly do
this part as soon as possible.
Then, the rest of the
part, which is animation
and autolayout, which I'll
be talking about on Monday
and maybe next Wednesday,
you can leave that --
you do that after
you do this part.
They're not so intertwined
that it's like, oh,
you've got to do them
all at the same time.
It's something that
you can do after.
Okay? Okey dokey.
So, we are going to create
a new project in Xcode here.
And, I'm going to call that --
I'm going fast through a lot
of these starting up things,
because you know how
to do this already.
So, I'm going to call
this one SuperCard.
One thing interesting
here, I'm not going
to specify a class prefix, just
so you see what that looks like,
because we've always
specified that, like card game
or something like that.
So, I'm just going to not
specify that, and we'll see.
I'm going to create it
in my home directory,
developer, as I usually do.
Here, you can go.
and see right here where
it says ViewController.h
and ViewController.m,
that's the name
of my view controller classes,
which is really --
those are bad names.
And that's why we usually
want to put something
in that class prefix, like
CardGameViewController.
Okay? I'm going to move
these down out of the way.
Otherwise, here's my view,
right, my storyboard view.
And I could bring up the
controller for it right here.
You can see I've
got a viewDidLoad,
which I'll go ahead and leave.
Memory warning, don't need that.
Okay. And all I'm going
to do in this view --
let's make this a little
smaller -- is put a single view,
custom view, which is
going to be a playing card
but a real playing card,
drawn playing card.
Okay? So, let's start by doing
what we always love to do,
but which is setting our
background here to moss.
I love moss; there it is.
Okay, there's our moss.
We like that.
I'm doing that mostly so you
can see what's going on better.
And, then I'm going to grab
this custom view out of here.
So, let's go down here.
UIView is pretty far down,
past halfway; there it is.
See right here, "view represents
a rectangular region which draws
and receive events,"
and you just drag it
out and put it out here.
I don't want it to be this big,
so I'm going to make it smaller.
It really doesn't matter
exactly what size I make this,
because I'm going to make
my class, my drawing class,
so that it'll draw properly
in pretty much any size.
Now, it's going to look pretty
bad if it's not, you know,
mostly tall than wide, because
playing cards don't look very
good if they're wide and short.
Okay? My view will
work; it won't --
people won't recognize
it very much as a card,
but it certainly would be fine.
And in your assignment, the
custom view you're going
to create, you also
want to make it that way
so that it works
in any size bounds.
And in that card, since this is
going to be a set card for you,
set cards actually could
make sense being sideways;
you would want the
three symbols, you know,
going sideways instead
of up and down.
So, a good solution
would make, really,
any aspect ratio look
nice for a set card.
But playing cards,
mostly only going
to look good, kind of, set up.
So, what we do here, then, is we
go to this identity inspector,
and here we want to set
our class of this view
to be some custom class, right?
Right now, it's just a
generic UIView; and so,
let's go create that class.
So, I'm just going to do File --
New File, just like we always do
to create a class, Objective-C.
This one's going to be a
subclass of UIView, okay,
instead of UIViewController
or anything else.
I'm going to call
it PlayingCardView.
Okay, this is going to be
a generic playing card--
displaying view.
Completely generic, not
tied to the Machismo model,
not tied to anything else;
it's just, like, standalone.
And remember that when we
create views, things that go
in the view camp, they want
to be as generic as possible
and reusable as possible,
so that I could use
this PlayingCardView
in my poker game app
and also in Machismo.
Okay? That's why -- we really
want to try and enforce
that kind of generic
nature of it.
So, here's PlayingCardView's
implementation.
You can see that I've got
intiWithFrame right here;
that's its designated
initializer.
And you can see I've got
the all-important drawRect
commented out.
If you don't comment --
if you comment this out,
and you don't put anything
in there, that's going
to give you a performance
hit, because it's going
to think this view needs
to be redrawn all the time,
when in fact it doesn't.
So, that's why it starts out
commented out, but we are going
to draw in here, so we will
be uncommenting it out.
I'm going to move this; we're
going to do something in there,
but I'm going to move
it down, out of the way,
because we're going to do
that at the appropriate time.
Okay, so any time we create
a new class, really important
to think about is public
APIs, so let's do that;
let's look at its public API.
It's a view, right?
What does it need to do?
Well, this playing card
needs to display --
PlayingCardView needs to
display a playing card,
so we better have some way of
specifying which card we want.
Now, some of you
might say, "Oh, great.
Let's use CardStar."
Or, "PlayingCardStar
would be great
because PlayingCardStar has
got everything we need."
But, again, I don't want
to tie this generic
reusable view to that model.
So instead, I'm going to have
NSUInteger rank, and I'm going
to have NSString suit,
and I'm also going
to have something that's not
even in that other model,
which is nonatomic BOOL faceUp.
Okay? I'm going to have it
so my card is either
face up or face down.
This is a playing card
view; it's got to be able
to display the card
in either up or down.
So, that's my API;
that's all I really need.
And so, we've just got
to implement that API
in our implementation.
One thing I'm going to do
right off the bat, is this.
Okay? Now, I typed this
really fast [whooshing sound];
that was it.
All I'm doing here, is all the
setters for all my public API,
I am calling setNeedsDisplay.
Okay? Because if someone
changes the suit, or the --
and yes, I could say,
"If underbar suit
does not equal suit"
and save myself a
setNeedsDisplay,
but -- okay, demo time here.
So I'm doing setNeedsDisplay
just to make sure,
if anyone changes the rank, or
the suit, or the faceUp-ness
of my thing, I tell the
system I need to be redrawn.
Do you all understand
this, why I'd do that?
Okay, excellent.
So, let's dive into
drawRect here.
I'm going to uncomment it out,
okay, and start drawing here.
Now, if I'm not -- I'm
going to do all my stuff
with UIBezierPath, so
-- well, actually, no,
I'll do a context here, just
to show you the context.
We'll do it a little
later, but we're going
to start off doing UIBezierPath,
and let's start with the outside
of my card, which I want
to be a RoundedRect, right?
I want my cards to
be a RoundedRect.
So, I'm just going to create a
Bezier path here, UIBezierPath.
I'm going to call it -- and
let me make some more space,
so we can see lots of code here.
RoundedRect, I'll call it.
And I do that with UIBezier --
actually there's a class method
that does that,
RoundedBezierPath --
BezierPath for RoundedRects,
down here, this one.
And I'm going to have
that RoundedRect be as big
as possible, so I specify
self.bounds as the rectangle
to draw this RoundedRect in.
Okay? Self.bounds is
my coordinate system.
Its width and height
is the amount
of space I have onscreen
to draw in.
Okay, cornerRadius,
I'm going to throw
in some magic code
here for that.
This is an important thing;
this cornerRadius is how
many points is in the radius
as it goes around the
corner of the RoundedRect.
And, really, that number
depends on how big my card is.
If I have a big card,
I want a big radius;
if I have a really small card,
I want a really small radius.
So, I've created this
corner scale factor,
which I've just standardized
to some height, and I can play
with these numbers to see
what works for all sizes.
And then, I'm going to pick a
radius that, at this height,
is 12; and that works
pretty nicely, right?
And then, I'm going to scale it.
Okay? So I'm going to call
this method right here,
cornerRadius, corner radius.
And you'll see I'm going
to use that scale factor
in other places, too,
to try and scale it up.
And this is part of
what I'm talking about;
you need to this thing to draw
in any size bounds
to make sense.
So, you're going to have to
have a little bit of stuff
that is dependent on, you
know, the size and height
of this thing to pick the
right size roundedRects
and things like that.
So, the first thing
that I'm going to do
with this roundedRect actually,
is I'm going to clip to it,
because I don't -- sorry
[random sounds] clip.
I'm going to -- I
don't ever want
to draw outside that
roundedRect.
Okay? That's the
interior of my card,
so I'm just going
to clip to that.
It also lets me do
something like this.
UIColor whiteColor, set my fill
color; and then I can just --
sorry, I do that a lot.
Then, I'm just going
to use this C function,
because I just want
to show this to you.
UIRectFill, and you specify
a rectangle, self.bounds.
Okay, this just fills
this rectangle.
So, it's kind of like shorthand
for creating that path,
instead of filling it
in, and all that stuff.
It just fills it, this
nice little UIKit thing.
And this is going to
make a big rectangle,
but this clip is going
to keep the white of this
on the inside of
that roundedRect.
So it's not going to draw the
white in the corners up there.
However, I'm -- got
a problem here,
in that my background color for
this view is by default white,
so it's doing this all
on a big white rectangle
that includes those corners.
So, I need to stop my
background from being white,
and I also need to
tell the system
that my playing card
is not opaque.
Okay? So, I'm going to do that
in initWithFrame down here,
but I'm going to do
it in the right way,
which is to have a setup method.
And in Setup, I'm going to set
my background color to nil,
which means that I don't --
don't draw a background for me,
and I'm going to set opaque
to no, I'm not opaque.
I'm also going to do
that contentMode equals
UIViewContentModeRedraw.
Remember I told you that
if my bounds ever change,
I want to get my
drawRect called.
Now, my bounds is not going
to change in this demo,
but I know that if it ever
does, that's what I want.
And then, really importantly,
I want to make sure I do this
in awakeFromNib -- okay, self
setup -- because, in fact,
in this demo, I am going
to be creating this
view in a storyboard.
I'm not doing alloc init on it,
so this is how I'm
going to get set up.
Okay? Question.
>> What's the advantage of
setting background color to nil,
instead of UIColor clearColor?
>> Yeah, so the question is,
"What's the advantage here
of saying nil versus
UIColor clearColor?"
And the answer is there's no
advantage, except for that,
really, what I intend here is,
I don't have a background color.
So you can argue --
it's just a style thing,
but there's no difference
in there.
Okay? So, now we're
nice and set up.
Oh, one thing I'm going to show
you real quick here is pound
sign pragma.
I don't know if you
guys know about that.
How many people know what
pound sign pragma is?
Okay, almost nobody;
this is awesome, then.
Okay, so if you do a
mark, a pragma mark,
you can put a little comment --
comment, like initialization,
like this.
And now, up here --
see where it says
"implementation
PlayingCardView?"
If I click on that, you see
how it's put a line here?
That's this dash is this line,
and then initialization
appears here,
and it's grouped these for me.
So, I can put, like
up here as well,
pound sign pragma, mark drawing.
And maybe up here, I could
put pound sign pragma,
mark properties.
And then my stuff really
gets nicely grouped, okay?
So, something to
think about there.
All right, so back to here.
So, now I've got
my background white
in a roundedRect,
which is great.
But I actually also want
to draw a little black line
around the edge of my card.
Okay? So how would I do that?
Very, very, very simple;
I'm just going to take
to set my stroke color, which
is UIColor blackColor setStroke.
And then I'm going to ask
this roundedRect thing,
this Bezier path, to stroke.
Okay? And this can have
the default line width,
which is probably one
point, which is what I want,
but I could make it
thicker, or whatever.
So let's go ahead and
run, and see what --
how things look so far.
Now, this is not going to work.
And I'm doing this
intentionally,
because you'll probably
do the same thing.
Let's put this down
here, to -- there we go.
Surprised that doesn't do that.
Okay. So, you can see that I
don't have my rounded corners,
and I don't have a black line.
Why is this?
Anybody know?
>> Because you didn't hook
up the class [inaudible].
>> Exactly, because I
didn't hook up the class.
Okay, so if I go back
to my storyboard here,
this is still a plain UI view.
You see? UI view.
So I need to change this
to be a playing card view.
Now, when I run, the system
knows that's a playing card
view, and I get nice,
rounded rects.
Okay? See my corners
cut off there?
It's all good.
And there is a little
black line around there.
It's hard to see,
but it's there.
Okay? All right, so we're
off and running here.
The next thing I'm going
to do is draw the corners.
You know, like where
it says ace of clubs?
And it's in the corner
is an ace,
and then underneath
is a club; and then,
down in the other corner
is upside-down ace,
and then clubs above it.
You know what I'm talking
about, in a playing card?
So, we're going to draw that.
I'm going to do that
by saying --
again, let's make
more space here --
I'm going to say
self drawCorners,
but do this in a different
method, drawCorners.
Okay. So, to do this, I'm going
to use NSAttributedString.
Okay? And what I'm going to do
is, I'm going to make the string
that I'm going to put in
that upper corner be ace,
carriage return, clubs.
Okay? So, I'm just going to put
that in the upper left corner.
Bingo, that's all I need.
But, I -- there's two
things I need to set
in my attributed string.
One is the font, and I'm going
to use Vervoord [assumed
spelling] font;
and number two is, I need the
paragraph style to be centered.
I want those two things,
the ace and the club,
to be centered on
top of each other.
I don't want them
to be left aligned;
that's going to look weird.
Especially like a 10 of clubs
would look really funny;
I want them [inaudible].
So, we didn't really talk
about how to set that kind
of paragraph-level alignment
and stuff in a attribute string,
so here's a chance to show you.
So, to do that, we use this
class called paragraph style.
Okay, paragraph style
specifies things
like the alignment
of a paragraph.
So I'm going to create a mutable
one that I can set things in,
and I do that with
NSMutableParagraphStyle,
alloc init.
And then, I'm going to set the
paragraph style's alignment
to be NSTextAlignmentCenter.
So that should mean centered.
And then I'm going to
set this as an attribute
on my attributed string.
The other attribute I need
is the font, so I'm going
to say a corner font here,
and -- whoops, font --
and I need, of course,
to use a preferred font.
And, you know, there's
some arguments here.
What would be the best --
U I -- the best style here?
You know, maybe Headline;
maybe even subHeadline.
But I'm going to try Body,
and we'll see how it looks.
And if I don't like it, I can
go change it to something else.
But there's something
really important
to notice about this font.
It's going to come
in in a certain size;
whatever size the user wants;
old people like me want
big ones, so I can see.
And younger people like you
probably want smaller ones.
But we still need to
scale it from there.
So, we want to use that
information, but then we want
to scale it again, depending
on how big our card is.
Big cards want big font;
little cards want little fonts.
Okay. So, we're going
to scale it just
by creating a new font here.
And we're going to do that.
Make sure I do this the way
that I predefined it here.
So, I'm going to do this
by saying corner
font, fontWithSize.
So, this is a method in font,
an instance method in font.
This is create me a
new font that's just
like the font I'm sending this
to, but with a different size.
And the size I think I'm going
to use is corner font current
size -- pointSize is the --
is the property in a font
that tells you what its
current point size is --
times that corner scale factor
that I used earlier, up here,
to do this cornerRadius, okay?
So this is this corner
scale factor;
I'm using the same factor here.
And, again, we'll see if that
turns out to be right; if not,
we can always change it.
So now, let's go ahead and
create this attributed string.
I'm going to call this the
corner text, and I'm --
it's just an NSAttributedString,
alloc, initWithString.
The string I'm going to create
is NSString stringWithFormat;
as I promised, % at sign;
carriage return; % at sign.
Okay. The first thing
is the rank.
So, I need self rankAsString,
because my rank is a number,
and I need it as a string.
So I'm going to go
up here and really --
whoop, look at that,
rankAsString.
So, same thing we did
before; little array
with that in there, self rank.
I don't think we need
bounds checking and stuff;
this demo is light
on bounds checking.
You know, good code
would protect yourself a
little better.
And then, here is the suit.
Okay? Suit, suit.
All right, so make sure I have
my square brackets right here;
self suit, no; and there we go.
So, there's the string.
And then, with the
attributes we're going
to set here is a dictionary.
And I need to set the font.
Font -- what's it called again?
Font -- NS [inaudible]
-- NSFontAttributeName.
And that's the corner font
that [inaudible] right there.
And then, I need
NSParagraphStyle attribute,
which is a new one you
haven't seen before;
and we'll call that
ParagraphStyle.
Okay, close curly brace.
Oops! Thank you.
[Inaudible] that.
Okay, semicolon.
Are we good there?
I think we're good.
Okay. So, there's
the corner text,
which is just this carriage
return thing, centered with the,
hopefully, good-sized font.
And now, I'm going to
get the bounds of that.
Okay? I'm going to
get the bounds,
then I'm going to put that in.
And there's two things
to the bounds.
One is the point, the upper
left-hand point of it.
And I could just make that be --
actually, I don't even
need a local variable;
let's just go straight
to bounds set origin.
So, I could make that
be zero-zero, actually,
but then my little A return
clubs might kind of bang
into my roundedRect a little.
So, I want to move it in
a little bit, but again,
it depends on how bit
the roundedRect is
to how much I want
to move it in.
So let's say CGPointMake,
how about self corner
offset, self corner offset.
And corner offset is
another little method I put
up here that's also related
to this cornerRadius.
You see, I took the
cornerRadius divided by 3.
That looks like that's
probably about right.
Again, these are numbers
you would want to play
with when you are
designing your UI;
these numbers seem
to work pretty good.
And then, the size of this
rectangle, I actually need
to ask the attributed string
to tell me corner text size.
You know, how big is that
A return clubs going to be?
Because I don't know.
Right? The attributed
string can tell me, though.
So, now I have a
rectangle, and I'm going
to drew the A return
clubs in, and it's going
to be centered horizontally
on it.
And all I need to do is
tell it to draw -- oops.
Corner text, drawInRect,
text bounds.
Okay? So now it's
drawing in that rect; so,
it's going to be doing the text
alignment inside that rectangle.
Okay? [Inaudible].
So, let's hope this works.
And it doesn't work.
Now, why does this
work -- not work?
Well, because we
haven't set any card.
This playing card view is not
displaying any particular card.
So, we need to go back to
our view controller and set
that card to something.
And a great place to do
that, for testing right now,
anyway, is in viewDidLoad.
So, we're just going to
pick a particular one.
We also need to make an outlet
to our card here, so
we can talk to it.
So we're going to start
here, and then go over here;
I'm going to control-drag
to create an outlet.
I'm going to call
it PlayingCardView.
Okay, so here's an outlet, so
you can see what this is about.
Okay. This is an
outlet, IBOutlet;
it's a PlayingCardView, so we
need to import PlayingCardView.
All right, so that
works; that's good.
We have our PlayingCardView,
so I'm just going
to set my playing card --
oops -- self.playingCardView.
I'm going to set its suit to
be hearts, and then I'm going
to set its rank to be king.
Okay, that's my favorite
card, king of hearts.
So, let's set this to hearts
-- oops, special characters.
There's a heart --
oop, missed it.
Characters; there's a heart.
Did I get it that time?
Yeah. Okay, so there's
a king of hearts,
so we're going to do that.
So, now we run.
And we should hopefully
get the king of hearts.
There it is.
And the sizes look
pretty good there.
Okay. Now, if I were
debugging this --
in fact, let's just do it;
let's make this card really
small, see what happens.
Let's try this.
And that looked good, too.
Okay, so I'm pretty
happy with that.
And then, I would also
want to go to settings
and set the fonts
bigger and smaller,
and make sure that still works.
But, you know, I'm so far
pretty darn happy with it.
Now, of course, I checked
this before I came today.
I'm not [laughter]
[inaudible] do these on the fly.
So, there we go.
Now, what do we need to do next?
Well, now we need to
put that king of hearts
in the other corner,
upside-down.
Okay, now you might think,
"Oh great, upside-down.
Well, how are we
going to do this?"
Turns out to be very, very easy
to rotate what you
draw in Core Graphics.
So, we're just going to
do that; we're just going
to do exactly what we just
did but rotate it upside-down.
So I'm going to go back here;
I'm going to do this same
thing, this drawingRect.
Okay? But before I
do it, inside here,
I'm going to rotate
it upside down.
First, I'm going to move down
to that corner, then I'm going
to rotate upside-down.
So, how do we move
over to a corner?
Well, let's use one
of these CG functions
that I told you you
needed the context for.
So, let's get a contextRef.
Context equals UIGraphics
getCurrentContext;
I told you that's how you
get the current context.
Now that I have it, I can use
CGTrans -- what's it called?
Anybody remember?
Translate something,
context translate,
or something -- there we go.
Translate CTM; CTM is our
transformation matrix.
Okay, so we're just going to
translate, which means move it.
So I'm going to move it
down to self.bounds.width
and self.bounds.height.
Okay, move down to
the corner there.
Oops! Did I type
something wrong?
Size, oops, size, size.
Okay, self.bounds.size.width,
self.bounds.size.heigth.
So, I'm moving down to the
bottom corner there; remember,
our origin is in the upper left.
And now, let's rotate --
there's a rotateCTM context --
and this is radians.
So, I'm going to go halfway
around; in other words,
upside-down, which is m underbar
pi; pi radians is halfway
around a circle; I want
it to be upside-down,
directly upside-down,
180 degrees from
where it was before.
And, that's it.
Okay? So, I've translated and
rotated what I wanted to draw.
And then, I'm just going to
draw the exact same thing again.
Bingo! Okay?
So, some stuff is really, really
easy to do in Core Graphics.
Okay? Rotating, translating
things; super-duper easy.
All right?
So, we're good there.
Okay, the next thing we want to
do is draw the face of the card.
Okay? That king of hearts face.
Now, the face of the
card is one of two ways;
either it's a face
card, like the king,
or it's just the pips, right?
Little, like the four of hears
is four little hearts in there.
Okay, so we have
to do both ways.
So, let's do the
face card one first.
Okay, so I'm going to do that
up here, back in drawRects.
So, I'm in drawRects.
And what I'm going to do,
I'm just going to kind
of have a design here,
where if I can find an image
that has the name of
the card, I'll use it.
Otherwise, I'll try
and draw pips;
otherwise, I'll draw nothing.
Okay, so it's kind of a fairly
safe way to go about it.
If I can't find the face card
image, then it'll just be --
I'll still have the corners,
but I won't have any drawing.
So, how am I going to do that?
I'm just going to
look up the image.
I'm going to say UIImage --
let's make more space again.
UIImage, face image, and
I'm going to say UI --
I can use [inaudible] image,
image named; then I'm going
to make a string,
[inaudible] the format.
And it's going to have
the rank and the suit.
Okay, self rankAsString,
self suit.
Okay. So, there's
the image names.
So, I'm going to look that
image up; so that would be,
like, J spades, right?
10 hearts is not going to find
10 hearts; there's not going
to be [inaudible] thing, but
there will be K clubs, right?
It'll find those.
So, if it finds the face image,
then I'm going to display it
in the middle; otherwise, I need
to draw the pips, basically,
because it's not a face card.
And, so let's put a
little -- draw pips here.
Okay, move that later.
So, the face image.
How do I do this?
Well, really I just need
to tell UIImage draw this
in the middle of my card.
But I want to move it in from
the edges a little bit, right?
Because I don't want
whatever the face image is,
like a king, to smash
my corners.
Okay? My little king of hearts.
So, I want to move it in
a little bit; so I'm going
to create a rectangle to scale
my image into, imageRect,
and it's going to basically be
my bounds, except for I'm going
to use this CGRect
inset method here
to inset it just a little bit.
And I'm going to inset it
by self.bounds.size.width
times 1.0 minus a new property
that I'm going to invent,
called face card scale factor.
So, the face card scale factor
is going to be, like, .9,
to be 90% of the
size of my card.
Okay, .9 would be 90%
of the side of the card.
And then, same thing with
-- oops -- size.height.
Oops! Heapsort new.
Height times -- same thing,
1.0 minus self.facecard
scale factor.
All right, and we have
to go make this property.
So, let's go do that.
Let's go up here, put it
in our properties second.
And I'll type it
really fast, again.
Sorry to have to use those, but
some things are boring to type.
All right, so I have this
face card scale factor.
It's a float.
I'm going to override both
the setter and the getter.
Okay? In the getter, I'm
going to make sure that it's
at least a default value.
Okay? 90% is going to
be my default value,
so it's at least going
to be that default value.
And in my setter, I'm going
to call setNeedsDisplay.
Because if someone
sets that scaling,
I got it to redraw my
card if it's a face card.
Okay? Everyone understand this?
And since I do both the
setter and the getter,
look what I have
to do; synthesize.
Okay? Everyone remember that?
If you put in both
the setter and getter,
you've got to synthesize
yourself.
Okay, good reminder of that.
Okay, so now I have this
imageRect, and it's inset
by that -- whatever that
percent is -- 10% if it's --
you know, 90 would be
1.0 minus that is 10%
of my width, 10% of my height.
And so, now I just say face
image, drawInRect, imageRect.
Okay? And it will scale
it to fit in that rect.
Now, I need these images,
so let's go grab those.
I happen to have them
handy right here.
Okay? So, here's
a bunch of images
that I have for face cards.
Let's just go ahead
and drag them
into our assets library
here, image assets.
So, here's our image
asset library.
We don't have any yet;
drag that in there.
While I'm here, I'm also going
to drag our famous Stanford one.
And, actually, let's get
the high-res one, too.
Okay, we'll use that
as our card back again.
Okay, so we got all
our images here.
So, the images -- look
at this -- I could --
let's put the high-res one in
for a king of hearts, at least.
So here's the king of hearts.
I have a high-res
for all of them,
but just for time
constraints, let's go ahead
and put this guy right here.
Okay, so we get that
one at least.
All right.
I'd want to drag them
all in for all of them.
But anyway, so now
I have these images.
So now, when I run, it's
going to go look for the image
of these names, you see?
And it's going to find
the king of hearts there.
Okay? So, that's
the suicide king.
Right? Looks pretty
decent; size is pretty good.
Okay. All right, so now we are
going to do two quick things.
We're -- I know we're right
on the edge of time here.
We're going to do the gesture
recognizers really quick.
So I'm going to do two things.
One is, let's make this
playing card view work facedown.
And we're just going to do that
by saying if we're face up,
then we'll do this,
all this stuff.
Otherwise, we will just do
our background image, UIImage,
image named, favorite card back.
I'm going to change it to that.
And then draw inRect,
self.bounds -- it's all --
and then for the back, I
don't have the corners.
And so, I can just go ahead
and use the whole
thing, so card back.
Okay, so now we have the front
-- the back there, rather.
So now, since it's face up
-- it starts out not face up.
So, here it is, but I
need to flip this over.
So let's do a swipe
gesture to flip that over.
Okay, and the swipe gesture
we're going to do using Xcode.
Okay? So, swipe gestures
go like this.
You go down here, you get
in the object palette,
same place buttons
are, and you'll see
that there's these
gestures here.
Now, I'm going to take a
swipe gesture and drag it
onto the view that I want
to recognize that swipe,
which is my PlayingCardView,
obviously.
Then, I simply control-drag
from this little icon
that appears down here.
You see it?
Swipe gesture.
You can also do it from here,
this little document outline
which we don't have time
to talk about today.
Okay, but you can do that, too.
But I'm going to
control-drag from here.
Oops! Go auto in controller.
And you just drag that.
It's very much like
target-action.
Okay. I'll call this a
swipe, just to make it clear.
And their argument is the
swipe gesture recognizer;
I'm not sure why it
doesn't default to that.
And we have the swipe.
And the swipe, all we need
to do is say
self.PlayingCardView.FaceUp
equals not
self.PlayingCardView.FaceUp.
Oops! Just flip it over.
Okay? Here it is.
I'm going to swipe, okay.
The default swipe direction,
by the way, is to the right,
so I'm swiping and you can see
that it's flipping the
card back and forth.
By the way, you can inspect
which direction the swipe is
by clicking on it and
going to the inspector.
Then there's right-left,
up-down,
so that answers your questions;
those are the only
four there are.
And how many fingers have
to be involved; one finger,
multiple fingers, etcetera.
Okay? And lastly, I'm going
to do a pinch gesture,
and I'm going to do
the implementation,
the pinch gesture,
entirely in the view, okay?
So I'm going to add a new method
to the view, which is pinch.
And it's going to be
UIPinch gesture recognizer.
Okay? Now, this pinch is really
simple; it's just going to say
if the gesture's
state is changed --
oops, not the [inaudible] --
changed, or if the
gesture's state is ended.
Okay? Then, I'm simply going to
take this face card scale factor
that I have and multiply
it by the gesture scale.
But I don't want that to
accumulate, so I'm going
to set the gesture's scale
back to 1.0 all the time.
So, the next time I get called,
I'll get the incremental scale.
Okay? So, that's all
that's necessary there.
I'm actually going to make
this public, because I want
to be able to add this gesture
recognizer in my controller.
So, let's just add this
here, just so my gesture --
so my controller knows that my
card view is capable of this.
Then, I simply go
and view didLoad,
and say self.playingCardView,
add gesture recognizer.
And I'm just going to create a
new UIPinchGestureRecognizer,
alloc, init.
You'll see that the -- this
is the designated initializer,
the target is going
to be the view,
and the action is
going to be pinch.
Okay. Well, that's it.
So, this is how you
add this in code.
[Inaudible] everyone
understand this?
So, I'm adding it to
this view, creating it.
This is the target; the
view is going to handle it;
It's going to handle the pinch.
We handled the swipe,
but the view is going
to handle the pinch.
All right, so let's
see, what does that do?
That should allow us, when we
have a card here, if I pinch,
to change the size,
because it's adjusting
that face card scale factor.
You see? Make sense?
But, all we need to do to add
the deck here is to go back
to our controller and add
-- well, first of all,
let me drag the model in;
I've got my model right here.
There's my model; same
model we used in Machismo.
And I'm just going to go
here and add that deck,
strong nonatomic,
deck star deck.
Let's go ahead and import
playing card deck [inaudible]
import playing card.
All right, so now
we have that deck.
Here is the lazy instantiation
of the deck; you
know about that.
I'm also going to have
this method I'm going
to call draw random
playing card.
Okay, this method is just going
to draw a card from that deck;
it's going to see if it's
a playing card; if it is,
it's going to cast it so that
we have a local variable.
Then it's going to set that
in the PlayingCardView's rank
and suit to the card's
rank and suit.
Okay? And I'm going to do
that every time we swipe
and the -- it's face down.
[ Typing ]
Okay? So that's that.
So, here it is.
So now when we swipe over,
we get different cards.
Okay, we have no pips yet; I'm
going to do that in a second.
But we do have the face
cards, the jack of diamonds;
kind of low-res because I didn't
have the other ones in there;
but king of clubs, okay.
Now let's do the
pips real quick.
The pips are super simple,
especially if you have
a little snippet here.
So, remember we have
this draw pips, okay?
I'm just going to paste that
with an implementation of that.
You can go look at this
offline, what pips does.
Pretty straightforward; it
just uses attributed strings,
or maybe even just
regular strings, to draw.
So, let's see what
that looks like.
So, there's our card.
And there we go; six of
[mumbling], diamonds,
four of clubs, seven spades.
And the last thing I'm going
to do; let's resize this view
and make sure it still looks
good small, even with all
of the changes we made.
So, we'll do -- make
it, like, this big.
And run. And -- well, it
still looks okay; a little --
a little squishy with the pips,
but I see a face
card; that's not bad.
Okay, that's it for today.
Sorry to keep you so long.
And I will see you
all on Monday.
>> For more, please
visit us at stanford.edu.
