[ Music ]
>> Announcer: Stanford
University.
>> Alright, well
welcome to lecture 17
of CS193P, Fall 2013/14.
And today I'm going to spend
just a couple minutes typing
up some loose ends from
the demo we had on Monday,
and then I'm going to talk about
taking pictures with the camera.
And then we'll do
a demo about that.
And that's basically PhotoMania,
what we kind of set this all
up for in our last demo.
Then we'll talk about
core motion,
which is the accelerometer and
the gyro and all that business,
and I'll have a demo of that.
And if time permitting
at the end here,
we'll talk a little bit about
the application life cycle.
We talked about the view
controller life cycle
where it appears and
disappears and all those things,
well the application
kind of, you know,
you come into the foreground,
you run, then you go away,
then you go the background,
you come back, so there's kind
of a life cycle there too,
which we can talk about briefly,
and I could even demo some
of that if we have time.
Alright, so let's
talk about the demo.
I'm going to go back to
the demo here real quick
and talk about two things.
One was cleaning up image URLs
because the way we do our
URLs is we have this "getter"
for image URL, and any
time someone wants a URL
for the image that's
in our image view,
why we just create a file
on disc and return the URL.
So it's kind of on-demand.
Well, the problem is, what if
they then take a different photo
or if this view controller goes
off screen and back on screen,
and someone picked
something else,
you don't want any
old URLs lying around.
So we need to clean
up those old URLs.
And the other thing I'm going to
show you, just a brief bit more
about checking for
errors in core location.
Okay? Because we did some
error checking already,
but we could do just
slightly a bit more.
So let's go back to PhotoMania.
And let's look at that.
The cleaning up the image
stuff is really two things.
One, when we cancel, okay?
When we do our cancel to get out
of our modal view controller,
we're going to set
our image to nil,
and why are we going to do that?
That's because if you look
at our set image, down here,
we delete the URLs, right?
So whenever we change the image
that's in our image view, right?
We delete the URLs.
This is something we did, kind
of it was a little rushed maybe
at the end, but we do
delete our URLs there.
And that's because we've got
a new image, so we're going
to delete the URLs for the
old image, and that will make,
we'll set our URL to nil, so
that we'll make new URLs, right?
So this is where we're
going to clean up.
So we definitely want
to set that image
to nil when we cancel.
And the other thing is,
when we don't cancel,
when we actually prepare
for segue and make a photo,
then we want to set
the URLs to nil,
so that they don't
get deleted, okay?
Because here, we're reporting
that URL back in the photo,
so we do not want
that URL to delete.
So we're going to
set these to nil.
So that they won't get
deleted at any point.
Okay? It's kind of like we
use them, we use that URL,
so now we don't want to-- we
want to just set it to nil.
So that was that, that's
the only clean-up there
that was involved.
The other thing was this
core location error checking,
and you can see, for example in
our should perform for segue,
I put a comment in here,
"should check location
and image URL" too, right?
Because I already checked
to make sure we actually
took a photo,
and that the person provided a
title, so I should also check
that the location, that
we got a location, right?
Because remember that
location we're kind of getting
in the background is
asynchronously looking at GPS
or wi-fi or something
to get that location.
So here, when we hit
"done," we've got
to make sure we actually
got one.
And more than just
determining that we got it,
let's make sure there
wasn't a particular error.
And so there's a way
to watch for errors.
Here's where we create
our location manager,
and we only use this one
delegate method right here.
But there is another
thing we could do,
which is we could implement
this delegate method,
didFailWithError and that will
tell us any time the location
manager fails.
Fails to get a location.
And I'm just going to salt
away the error code that comes
in this NS error into
this location error code,
which I'm going to
make a property.
Okay so it's just an integer.
So I'm going to keep
that error code,
and then down in done right
here, okay, I am going to,
in addition to checking
the title
and whether a photo was
taken, here I'm going
to check the location.
And so if I didn't get a
location, if location is nil,
then I'm going to look at
that error code that I got.
Now, it might be zero, which
is error location unknown,
in which case, what does
that mean if the location--
if either I've never gotten
an error, or I got an error
and it said location
unknown, which is zero, well,
that means that it's
still trying.
Okay? It hasn't gotten an error
that says I can't get the
location, but it's still trying,
so that's what we're
going to tell the user.
It's like I couldn't figure out
where this photo was taken, yet.
Okay? And if they say this is
an alert, so they'll click "ok"
and they'll be back
where they started,
they can try hitting done again,
and they just keep hitting done
and seeing if this keeps
coming up until they get tired
of it and they hit cancel.
Okay? So that's one
error situation.
Another one is "denied."
This shouldn't happen because
we checked whether we were
authorized before, at the
very beginning remember?
And we put a fatal alert up
if you weren't authorized.
But it's actually possible
they could put this alert up,
go to settings, disable it,
come back, and click "done,"
and then they'd be denied.
Okay? So that's pretty rare,
but let's check for it anyway.
And if they do that, then
we'll just tell them,
go back into privacy and turn
that thing back on, okay?
So we could get a denied,
rare, the other one is network,
so this is-- we're trying
to find the location,
we can't get any GPS,
and they're not connected
to the network, so we
can't use wi-fi either.
Okay? Or a cell site
for that matter, okay,
so now we're really
stuck and the thing is
that the location services
can detect that case,
and they'll actually
report this error, network,
and we can tell the person still
we can't-- same basic error,
we can't figure out where
the photo was taken,
but at least we can suggest
that they verify your
connection to the network, okay?
And oh, I forgot to turn wi-fi
on, they turn wi-fi on, and oh,
now it's able to figure
out their location.
And otherwise, we're just going
to generically complain, okay?
That we couldn't figure it out.
Okay? So I just wanted to show
you a little bit about getting
that error code using this
didFailWithError thing.
And in general, in demos
that I do in this class,
obviously I hardly check any
errors, it's just a time thing.
You should, any time you
get an NS error back,
you should go check
it and see what it is,
and go look in the
documentation and see what kind
of errors you could get back and
what you should do about them.
Should always do that.
I try to emphasize in the slides
when errors are really important
to check, but in general, we
should always be checking those.
Okay? So that's it, that's all
I wanted to show you there.
So back to our slides.
Any questions about that?
Yeah?
>> Does Apple provide a
service where you could log,
sort of the statistics on the
errors you get in your app
and you could [inaudible]
some of it?
>> So the question is, does
Apple provide a service
where you can like log
the errors you get,
for what purpose, to kind of so
that they know what
errors people are-- ?
>> Well to know what kind
of errors [inaudible].
>> Oh, I see what you
mean, so you want to--
you have customers for your
app, and they're having errors,
and you want to find
out about them.
Apple doesn't provide
that infrastructure,
but there are plenty of
third parties who do.
That basically provide
code you put into your app
that provide metrics,
not just errors,
but how many times did
people click on this button?
How many times did they go to
this page, you know, et cetera,
so you can see how
people are using your app.
That's actually quite important.
Obviously beyond the scope of
this app, or of this lecture,
to talk about it, but I really
highly recommend putting metrics
in your app so you really
understand how your real users
are actually using your app.
It's a little more work for
you, but it's really worth it.
Um, okay, so...the camera.
So all this camera business
and basically getting images
into your app is done with this
class UIImagePickerController,
which is a view controller.
Okay? It's a UI view controller
that you're going to put
on screen, either
modally or in a popover,
depending on whether you're in
iPad or iPhone, and you're--
so this is the first time you're
going to have a view controller
that didn't come
out of a storyboard.
Okay? This view controller
you're going
to alloc init, alright?
And then you're going to
want to put it on screen,
so you're going to have to use
this method that we did talk
about weeks ago when we talked
about presenting view
controllers called
"present view controller,
animated."
Okay? Completion.
It's got a completion
block as well.
And so that's how we're going
to put this thing on screen.
And the argument to present view
controller is a view controller,
not a storyboard identifier
for a view controller,
but an actual instance of
a view controller, okay?
And then the iPad
you're probably going
to put the camera-- actually the
iPad you probably put the camera
up full-screen modally,
if you're just looking
at the photo library,
you're going
to probably put that
up in a popover.
Alright, so how does this work?
You alloc and init this
UIImagePickerController,
you set it's delegate, you
have to set it's delegate
or you're not going to get
any information out of it.
Then you configure it for
what you want, the camera,
or photo library, things like
that, whether you're going
to let the user edit the
photo after they take it.
Then you present it, and
listen to the delegate,
and when the delegate tells you
that the user has either
canceled or taken a photo,
then you, if they've taken a
photo you get the image data,
and if they cancel you're
like "Okay well they're done."
Alright? What the
user can do, okay?
In this UIImagePickerController,
it depends on what
their hardware can do.
Almost everything has a camera
now, so you're usually--
if this is going to be no
problem, source type available,
camera, you're going to find it,
but you should always
check this anyway.
Just to kind of,
to be good code,
you want to call this
UIImagePickerController,
class method is source type
available, and we actually did
that in our demo last time.
Remember? We had that
class method can add photo,
and in there we checked
to make sure
that the camera was
actually available,
so this is how you do it,
just this class method,
UIImagePickerController.
Once you know the camera is
available, then if you want
to take video, you need
to be able to make sure
that camera can do video,
because there are cameras
in older devices that
can't take video,
they can only take images.
And you do that with this class
method, "available media types
for source type," and the
source type is the camera.
Okay? So it's going
to tell you, what are,
it's going to give
you an array back
of what things this
camera can do.
And you're only going to
have one of two or both
of these two constants, UT
type image, and UT type movie,
unfortunately, these
two constants come
from MobileCoreServices,
MobileCoreServices.h,
they're not in foundation
or UI kits,
so you have to explicitly
import this thing.
You've got to link in
the MobileCoreServices,
but with the new mechanism in
iOS7, that should get linked
in for you automatically.
And unfortunately, those
two things, kUTType image
and kUTType movie
are not NS strings,
they are core foundation
strings, so we'll talk
about how we deal
with that in our code
in a couple slides here.
You can also find out
about front-facing
versus rear-facing cameras,
all kinds of details
about what your hardware
has, just take a look
at the documentation for
UIImagePickerController
and you can find that all out.
Is that a question?
>> Oh, does the view controller
provide the ability for them
to select which camera they're
taking the picture with?
Or do we have to set that
before we display it?
>> Uh, the view controller
will provide UI for them
to pick front camera
or back camera,
but you can also say only let
them take a selfie, right?
Only for, or whatever,
you know what I'm saying?
You can just determine it,
but they also have the--
if you don't determine
it, then they can choose.
So the answer is yes.
Alright, so this
is what it looks
like to configure the
ImagePickerController here,
so I'm creating one,
I'm setting my delegate,
I'm checking the source
type to see if the camera,
I want to do video here,
so I'm going to make sure
that it does video, and if
it does, I'm ready to go.
Okay? So that's a summary
of what I just said,
kind of in-code what I just
said on the last few slides.
Um, so let's talk about
that kUTType movie
and kUTType image,
they are CF strings.
You can just cast
them to NS strings.
There is a bridging mechanism
between core foundation
and foundation, which
is what you're used to.
And so you can just put this
cast in here, and in iOS6 or 7,
they introduced a thing where
it will even do this bridging
for you automatically in
terms of arc and all that,
so you just need to cast that,
it's just something you need
to know, is to put
the NS string star
in front of those two types.
Alright, editability-- so
when someone takes a picture
with the camera, they can
actually zoom in on it,
and move around and pick what
part of the image they want
to report back to your
app, if you allow that.
And you have to allow that
by saying allows editing
to your UIImagePickerController
instance,
and you can also do things
like limit the video capture
to be low resolution or
only a certain number
of seconds, things like that.
So I'm not going to go through
every single thing you can do
in UIImagePicker, but
there's tons and tons
of stuff you can do with flash,
all these other things
you can control.
So then, you have this
UIImagePickerController
that you've instantiated
yourself as the delegate of,
now you present it, okay?
And so on the iPad, if you're
not offering the camera,
so if you're-- sorry, we kind of
glossed over this a little bit--
you can offer to get a
picture from the camera,
or you can also offer the
user to have a picture come
from their photo library, okay?
Which is this thing they
manage in app on their device.
And so you can kind of
pick either or both.
On the iPhone, you can ask
for both at the same time,
so it will put up a UI
where you can kind of--
the user can pick
between the two.
On the iPad you don't
do it that way.
On the iPad it's
camera or photo library
because the photo library you're
going to bring up in a popover,
the camera you're probably
going to do full-screen modal.
Okay? So that's why you're
usually not mixing them
at the same time.
After the user has chosen a
photo from by using the camera
or from their photo
library, you're going
to get this delegate method,
ImagePickerController,
FinishPickingMediaWithInfo.
Okay? And you are then going to
dismiss that view controller.
Okay? So you have to manually
dismiss it right here,
but before you dismiss
it, you're going to look
into that dictionary that just
passed, this info dictionary,
and get the information
about what camera,
what picture they
took or whatever.
If they didn't, if they
hit cancel on the camera,
then you're just going
to get this
imagePickerControllerDidCancel,
and there's no dictionary
there, so you don't get
to find anything
about the photo.
Again, if you're in a
popover, you're going
to use the popover did
dismiss popover business,
you'll do the same thing.
So what's in that
dictionary that you get back?
So if the user did finish
picking with info, what's in it,
there's the image
that they took.
Both the edited image, if
they zoomed in and cropped it,
and the original
image, if they didn't--
whether they did or not, the
original image is in there.
If they cropped it, it will tell
you the rectangle inside the
image that they cropped to.
If it's a video, you're
going to get a URL to a file,
with the video in there, okay?
So that's how video comes back
to you, as a URL to some file.
And you can, if you get
these images, and you want
to save them, like in the
user's photo library, that's--
API is in AL assets library.
Not going to cover that,
but that's where the API is,
if you want to save it
into their photo library.
Some picture they've taken.
Like in PhotoMania, we're
not going to do that,
we're actually going to save
it into our own database, but--
and we're going to
save our image
into our documents directory,
but we could put things
in the photo library as well.
The camera can have an
overlay view on top of it.
Okay? This is a view
that sits on top of it.
You can even provide
your own buttons for like
"take picture" and
things like that.
This is just a view,
and you also are going
to get this transform,
see at the bottom there?
Camera view transform, in
case you want the image
that the person's taking,
for example, to be scaled
up to full screen,
because the aspect ratio
of the phone itself is not
the same as the camera,
so it won't be full screen.
You'll see there will kind
of be some borders around it,
when people like to zoom
it up to full screen,
you could do the
transform to do that.
If, in your overlay view,
you provide the "take picture"
button, then you want to say
"show camera controls,
no" otherwise you'll have the
take picture button from iOS
and also your own
take picture button.
Okay? So overlays, you can
look those up offline as well.
We're not going to
do those in the demo.
Let's go ahead and add this
photo taking to PhotoMania.
And this action sheet thing,
we'll see how the time goes,
whether we do the action sheet.
Alright, so here we
are in PhotoMania,
and if you remember here,
let's go ahead and go
over here...and remind
ourselves what it looked
like right now...just...take
this around...Alright
so we have our photos here, we
haven't taken any photos yet.
Hopefully, this is probably
loading in the background
as well, yeah, there
it is, from Flickr.
But if I look at my photos,
I can see I don't have any,
but I have the camera in
the corner, so I'm going
to press the camera,
we put that photo in.
And see you notice it
saying "PhotoMania would
like to use your
current location" okay?
And I'm going to say
"Ok," if I said "no,"
we probably would have to
put up this fatal error
that says you can't
take a picture.
And what we want to do here is
you see the "take photo" button?
I want to be able to press
that "take photo" button
and have this
UIImagePickerController come up.
Okay? So that's what
we're going to do next.
Alright, so take photo,
we actually wired that up
when we very first started
here, where did we put it?
It is right...here.
Okay? And it doesn't
do anything right now.
So what do we need to do here?
All those things I just
told you in this slide.
So let's do a
UIImagePickerController,
UIImagePickerController, UI,
image picker controller equals
UIImagePickerController,
alloc init.
So we just alloc init, there
is no other thing we specify
when we initialize it,
that's all we need to do.
And then we just need to set
up this UIImagePickerController
to specify any options we have.
And we also need to set
ourselves as the delegate.
So I'm going to do that first.
And you're going
to see something
interesting about this.
So we know that as soon as we
set ourselves as a delegate,
we're going to get this warning
that says we don't implement the
UIImagePickerControllerDelegate
method.
Notice it also says
we don't implement the
UINavigationControllerDelegate.
Okay? This is kind
of a weird thing
about the
UIImagePickerController,
it inherits from
UINavigationController,
so it inherits that delegate
method, and so it requires
that you be a delegate for both.
So I have to be
NavigationControllerDelegate,
and I have to be the
ImagePickerControllerDelegate.
Now neither of those things
have any mandatory methods,
so it's not that onerous but
this is just an error that a lot
of people are like, "Huh?
Why is it saying that?"
It's just a weird thing about
UIImagePickerController,
you have to be a
NavigationControllerDelegate.
You're not going to implement
any of those methods,
just have to say you do
implement that protocol.
So now we're the
delegate, and we're going
to implement a couple of those
methods in a minute, but first,
I'm going to finish
configuring this thing.
So what else am I going to do?
Well, I'm going to say
what media types I want,
and the only media type
I want is an image.
I don't want video, so,
kUTType what's it called, image?
Image. Yeah.
Okay? So this is an array,
but the only one I'm putting
in there is this one thing,
and I'm doing this weird cast.
Okay? And then what else
do I want to set up here?
I want to set up
the source type,
which is I want the camera.
So this is
ImagePickerController,
SourceType, Camera.
Now, I could also say here
vertical bar UIImagePicker
SourceType PhotoLibrary, okay?
On the iPhone this
would be fine,
where the UI would let me choose
from either of these two places.
I don't have anything
in my photo library
on my demo machine, so
it's kind of a waste.
And on the iPad I would
have to do two separate UIs,
so I couldn't really
do that there anyway.
I'm also going to
allow editing, why not?
Allows editing equals yes,
so that allows me to crop
and zoom in, allows the user to
crop and zoom in on the image.
And that's it.
And now I just do this
PresentViewController, okay?
Animated, yes.
Completion-- completion block,
again, it would get called
as soon as the view did appear
happened, so in other words,
once it was on screen,
this would get called,
I don't need to do
anything then.
So we're done.
Okay? So this is going to
cause a modal view controller
to appear, and it takes over
the screen, and it's going
to be the camera, and the user's
either going to have the option
of using the photo they
take or hitting cancel.
So we have to handle
both those cases.
So let's do the cancel case
first, because it's really easy.
And we do that with this one
right here...this is right here,
ImagePickerController DidCancel.
You can see there's
only two methods here,
there's an old deprecated
one there,
but there's only these two.
Those are the two we're
going to implement.
So in cancel, our
only responsibility is
to dismiss this thing.
Okay? Because we're not going
to use the photo or anything
like that, so I'm going to say
dismiss, yes, completion again,
I don't need the
completion handler there.
Okay? So that's an easy one.
And then the other one,
this guy...this is the
DidFinishPickingMedia WithInfo,
so we've got this
dictionary right here,
that provides the information
about the photo that
the user chose.
So the user did choose
a photo in this case.
So this one's easy too.
We're just going to
say image equals info
UIImagePickerController,
edited image.
Okay? So this is a key
into this dictionary.
Right? And that's
the edited image,
the UI image the user edited.
Now, I'm also going
to say if not image,
then the image equals the
info UIImagePickerController
original image.
Okay? Why am I doing this?
Because I allow editing
right here, okay?
So I know I'm going to
get this edited image.
Well, someday I might
turn this to no,
and then my code
here would break.
So I'm just trying
to make code here
that would work in either case.
It costs no skin off my nose to
put this line of code in here,
and it will make it so
if it ever changes to no,
then this is going
to still work.
Okay? Doesn't hurt
me if I don't.
Alright, so I got the
image, so I'm just going
to say help equals image, image,
remember that we have
this property set
that we implement the setter and
getter for, which is right here.
Okay? Which is just setting
the image view's image.
Okay? If we're setting
it to something new,
then it's deleting the old URL,
we showed that already today.
So...we set our image, and then
we dismiss [tapping sounds].
Okay? And that's it.
So using the camera,
quite straightforward.
Once you understand that you
have to do this delegate thing
to get the results out of there,
it's pretty straightforward.
So let's try and see if
we can make this work
[background sounds].
Let's-- here something to
take a picture of here.
Alright, so we'll go to My
Photos, I'm going to go here,
and I'm going to take a photo.
We're going to get this
ImagePickerController that's
going to come up Modally, so
here's my picture controller,
so we'll-- 2002, that's
kind of old, but take it.
So I'm just going to
take this picture.
Alright? That's a little blurry,
so I'll take another one.
That's a little better.
And I could zoom
in here, if I want.
Crop it, because I've
allowed user editing, right?
So we can do that.
And then once I got it the
way I want it, hit "use photo"
and it comes back and
sets in our image view.
One thing we also need to
do is put in a title there,
but that's...let's
do that real quick.
Giants. Alright.
Okay. There we go.
So now hopefully our photo,
there's our thumbnail,
if we click, we can
see the detail of it.
Okay? Any questions about that?
It's pretty straightforward,
the camera thing.
Alright. Let's see
what time we've got.
Let's...I'm going to come
back to doing the action sheet
at the end, because
I really want
to show you this other stuff
first, the core motion stuff,
and the action sheet, I think
you mostly understand that,
so we'll come back
to this later.
So let's go keynote here, okay.
Alright. So core motion.
So, core motion is the API
for accessing the hardware
on your device that
tells you about motion.
Okay? And there's a lot of
different pieces of hardware,
actually, that senses
motion on your device,
and some devices have
more hardware than others.
Okay? So a lot of using
core motion properly is kind
of understanding what your
device has, and how you're going
to use it, and if it
doesn't have things,
can you still do
what you want to do?
Maybe not quite as
nicely, et cetera.
But, the primary
inputs to where--
to the motion of your
device, are the accelerometer,
which is just telling you the
acceleration of your device,
in X, Y, and Z. A gyro, which
is letting the device know
when it's being rotated, which
is an important thing for it
to know, and magnetometer,
which is telling it
"where is true north?"
Okay? So really it knows
where magnetic north is,
but if it knows your GPS
location, then it knows
where true north is as well.
So...the class that you
use to get this information
about your device is called CM
Motion Manager, kind of similar
to this CL Location
Manager, which is how we got
where the device
is in the world,
and you create it
just with alloc init.
You have to be a little
careful with this thing,
because you can't really
have two different places
in your app alloc initing one
of these things and asking
for different information
rates or something like that,
because the rate
at which it's going
to report its information
really is something
that your whole application
needs to be
on the same page about.
Okay? So you wouldn't
want one place
in your application getting
data really really fast,
because it's doing some
really fine-tuned thing,
and the other wants
it really slow,
and then they're
kind of out of sync.
So some people would argue
that CM Motion Manager
should just be global.
You should have some global
somewhere, some class variable,
or your app delegate
or something.
I don't know that that's
strictly necessary to go
that far, but it is something
where they can fight each other,
so you've got to be
careful about doing that.
And that's just really because
there's only one device,
and there's only one gyro
and one accelerometer,
and so it makes sense that
this is a global resource,
in a sense.
So how to use the
Motion Manager?
Just like Location Manager,
you check to see what hardware
is available, then you kind
of have two choices to
how you want to do it.
You can set some sampling going,
and then poll the
Motion Manager.
Okay? What's the current
acceleration due to gravity
in these three directions,
for example.
Polling we don't like.
I have a slide on that, but you
can see I'm going to blast right
by it, because polling-- not
so good-- as the second way,
which is that you basically
put a block on a queue
that will get called every
time, at whatever rate you say,
to tell you here's the current
acceleration to gravity,
here's the current...just tell
you over and over and over,
and you can set the rate
at which that's happening--
much better way to do that.
Especially since you can have
that block be executing
on another queue.
Okay? Collecting the data,
coalescing it, whatever,
and then communicating
back to the main queue,
maybe at a lower rate,
where it's updating the UI.
Okay? So mostly that's what
we're going to focus on,
is the way of posting
a block to a queue.
Okay? So how do you
check the availability?
There is this property
in CMMotionManager called
accelerometer available,
gyro available, magnetometer
available,
and then the very important
device motion available,
and we will talk about
that in a second.
How do you start
up these sensors?
If you don't call start
accelerometer updates,
it will not give
you any updates.
And when you poll, you
won't get any update--
any information that's
up to date.
So you have to start it.
Now, normally we're not going
to start it this way in poll,
we're going to start
it by giving it a block
to go call us every time,
you know, at whatever rate.
So, but we do need to start it.
It's important to understand
you have to start it.
If you don't start
it, you get no data.
You can also find out,
is the hardware currently
collecting data?
In other words, is
the accelerometer
on and collecting data?
Is the gyro on and
collecting data?
So you can find out
that as well.
And very importantly, you
can stop it doing this.
Why is that important?
These things aren't free when
it comes to battery, okay?
Measuring the acceleration,
measuring the gyro, position,
all of that, it takes power.
And so you want to turn it
off when you're not using it.
Just exactly like the
CLLocationManager, right?
Turn it off when
you're not using it.
So here's the polling thing.
I have this slide, we're not
really going to look at it,
but for each of the
things, accelerometer, gyro,
magnetometer, and device motion,
which is the magic one I'm going
to talk about in a
moment here, you can poll
and say "what's the
current state of this?"
But you don't usually do that.
And now I'm going to talk about
the magic thing before we talk
about posting the blocks on the
queues, which is CMDeviceMotion.
Alright? If you had a
gyro and an accelerometer
and a magnetometer,
you really know a lot
about this device's position.
Okay? Really a lot!
And in fact, you
can use multiple
of those devices together
to get better information.
For example, let's talk
about acceleration.
Really, there's an
accelerometer in there,
it's always measuring the
acceleration due to gravity,
which is 9.8 meters
per second squared.
So if you ask the raw
accelerometer what's the current
acceleration of this device?
It's always going
to give you 1 G
down toward the center
of the earth.
Okay? No matter what's
actually happening.
Now, if you are holding
your device like this,
and you're going like
this, you're also going
to have acceleration
in these other axes,
but you're always going to
have that one pointing down.
So it's kind of like it's a
little bit difficult for you
to then figure out what's
the user actually doing
with my device?
You see? Because you kind
of have to filter out with
like a low pass filter, the
acceleration due to gravity.
Well, you don't have to do
that if you have a gyro.
Because if you have a gyro,
you know whether the
device is tilted now, okay?
And since you know whether
it's tilted, you can factor
out where the acceleration
due to gravity is, you see?
Right? And so the CMDeviceMotion
is another thing you can ask
for, just like you can ask for
the accelerometer or you can ask
for the gyro, you can ask for
this magic CMDeviceMotion,
it's actually a conglomeration
of those other things.
Okay? And so for example
it can tell you what is the
acceleration of the device
with gravity taken out.
So there's a property
in the CMDeviceMotion
called User Acceleration,
which is the acceleration of the
device, not including gravity.
You can also find out where
gravity is pointing based
on the device's current
orientation.
Similarly, there's bias in there
for rotation of the device.
Okay? Bias due to
electronics of the device.
That can be taken out
using the accelerometer.
So the gyros reported
information can also be helped
by the accelerometer, so
they kind of help each other,
and you can also get more
sophisticated answers
about the position
of the device,
like roll, pitch and yaw.
Okay? You all know what
roll, pitch and yaw is?
Roll is this way, yaw is like
this, pitch is like this, okay?
So now you really
can find out where,
like almost this is
an airplane, right?
You can find it's roll,
pitch and yaw in space.
Okay? And those are all possible
when you know where gravity is,
if you know where true north is,
you kind of have a reference
point, all those things.
So CMDeviceMotion,
that's where you're going
to do any sophisticated analysis
of things to position in space.
Okay? And you can go look
at the documentation,
you can see already
right there some
of the properties you can
get, roll, pitch and yaw,
rotation rate, user
acceleration.
All those things come
out of a CMDeviceMotion.
Now, if you're on a device
that doesn't have a gyro,
the CMDeviceMotion
is going to be there,
but some things are going to
give you unspecified results,
like user acceleration can't
be determined on a device
where there's no gyro.
Okay? It will give you the
acceleration due to gravity,
but can't give you
the user acceleration.
So you have to know.
That's why you have to
check what your device has.
So...how do we do
this block thing?
Okay? So I want to start my
device, checking what is going
on with the accelerometer,
and I want it to report
to me what's going on.
And the way I do that
is I give it a block,
a CM Accelerometer
Handler block right there.
You can see how it's
defined, it takes an argument,
which is the CM Accelerator
data, and in this error,
and it just keeps calling
that block over and over
at whatever rate you tell it to.
And we'll talk about how you
set that on the next slide.
And you can do that for
accelerometer updates,
you can do it for gyro
updates, you can do it
for device motion as well.
Okay? That roll, pitch and yaw,
all that, it will tell you that.
Okay? So couldn't be simpler,
you just call this method start,
accelerometer or gyro, updates
to queue, you give it a queue.
Now, that queue, if your rate
at which you're asking it
to tell you is pretty low,
that can be the main queue.
And what do I mean by low?
Well, use your best judgment,
I mean 60 times a second?
That's going to be
pushing the main queue.
Ten times a second, no problem.
Okay? It depends on what
you're doing in that block.
Hopefully you're
not doing some very,
very expensive graphics
operation.
But you could make
that the main queue.
Or you could make that
be some other queue.
Okay? And then coalesce events,
and then just dispatch back
to the main queue whenever you
want to do the UI updating.
Okay? So how often does
this block get called?
This is the device
motion ones, by the way.
Device Motion knows so much
information it can actually have
a reference frame about
where's Z axis and X and Y axis,
and all that, so you can
look that up as well.
But it's the same way, though,
in terms of you just put a block
on the queue and when
device motion has changes,
it's going to tell you.
So in all these cases, you set
the interval at which it's going
to call your block using this
update interval property.
So whether you're doing
accelerometer, gyro,
magnetometer, device motion,
you just set the interval.
This is in seconds, so if you
want it 10 times a second,
you would say 0.1, if you want
20 times a second, 0.05, okay?
How often can these things go?
There are limits.
These things can't report
thousand times a second,
probably you couldn't do much
with 1,000 times a second,
you couldn't respond in
the UI or anything to that.
I think most of these things
are around 60 Hz, okay?
60 times a second, probably
about the maximum you're
going to get out of them.
I don't think the
documentation specifically says.
It is okay to have
multiple handler blocks.
In other words, to call
start accelerometer updates
with block, give it a block,
and then later somewhere else,
start accelerator
updates with block
and give it a different block,
and both of those
blocks will get called.
They're both going to get
called at the same rate,
this update interval,
but two different--
you could be doing two
different things every time this
interval happens.
That's perfectly
fine, say it's not
like there can only
be one block.
Make sense?
Alright, so I have a demo.
This is all definitely best
understood with a demo,
and this demo lets me show you
a couple other things as well,
as usual, I try to
make these demos count
for multiple things
here, so this is going
to be a new app we're going
to write, it's called Bouncer.
And Bouncer is going to be a
little square that I'm going
to have appear on my device,
and eventually we're going
to have gravity determine
where that square is animated
to on screen, then we'll
put another square on there,
and then we'll bang those
things into each other,
and maybe we'll put a
little score in there,
and then we've made
ourselves a little game.
Okay? So it's a little,
basically game where the input
to the game is the
accelerometer.
So I'm going to use
accelerometer,
because it's the simplest
of the device motion things.
But they all basically
do the same approach
of how you get the
information back.
So we use the accelerometer to
drive our UI, just like touching
and swiping, or whatever,
we're going to use the
accelerometer as an input.
Alright, so let's make
a new project here.
Whoops! Not a new file.
Cancel. A new project.
Okay. And we're going to
call this project Bouncer.
And it's going to be universal.
I'm also going to do
something with this app
that I haven't done all
quarter, which is I'm not going
to do anything in
the storyboard.
Okay? We're going to do all
of this 100 percent in code,
just to see what that
looks like, okay?
So I'm not even going
to touch my storyboard.
Alright, in fact, I'm going
to move my storyboards
down to supporting files,
because I'm not going
to use them, okay?
It's all going to
be in this one file,
the entire implementation
we're going to do here.
Alright, so what are we
going to do to start?
Let's start by just putting a
little red square on the screen.
So we'll start simple.
I'm going to have a little
property here, non-atomic,
strong, just a little UI view,
I'm going to call it red block.
It's going to be a
little red square
that I'm going to put on screen.
And I'm going to do it, I'm
going to put in on screen
in ViewDidAppear, so once
this view appears on screen,
then I'm going to put this
little block out there,
and I'm going to do that in
a method called Start Game,
so it's going to be
the start of my game.
I mean, eventually
it's going to be more
than just a red block there,
so we'll say we're
starting our game.
So what do we do in Start Game?
I'm just going to say self
dot red block equals--
now I would need to create
a block, I want to put it
in the center of the screen,
so I create a nice little
method here, called add block,
this little guy right here, and
all it does, as you can see,
is it just does init with
frame and add subview.
And all it's doing is
calculating where that frame is.
So it's putting in
the center offset
by anything I want,
any UI offset.
So I'm going to put it
initially right in the center.
By the way, we don't need this
or this for this entire demo.
Alright, so I'm going to do
self, add, block, offset,
and I'm going to do UI
offset make zero zero.
So it's going to put
it right in the center.
Okay? Everyone understand what
this does, is just putting--
adding UI view to the
center of the screen.
And my block is red,
so let's make it red.
Dot background color
equals UI color, red color.
Okay? So let's go
ahead and run that.
Okay. Okay, alright, good start.
So what we want now is
I want some gravity.
I want this block to go
somewhere else besides
in the middle of the screen.
So we do that with something
you're all very familiar
from assignment 4, which is
animators, dynamic animator.
So I'm just going to add
a dynamic animator here
to add gravity and to make it so
when the block gets to the edge,
it's going to bounce off, okay?
Instead of just going
off the screen.
So let's do that.
Uh, that, I have since you
all know about animator,
I'm not going to type all
that in, I'm just going
to show it here, this
is what I just added.
Right here.
Okay, I've got some properties.
The properties I
have here are...
a dynamic animator, of course,
I've got a gravity behavior,
that's going to be the gravity
pulling on my red block.
Collider, that's just going
to be the outer edges,
that's the only collider
I'm going to have for now.
And then elastic is
an item behavior.
I want it to really
bounce off the walls.
So I'm going to set
it's elasticity
so that it's completely elastic.
All of it's hitting the walls
are elastic collisions, okay?
And so here's the animator.
Lazy instantiation not doing
anything special there,
here's the collider, okay?
The only thing that does is
translates reference bounds
into boundary.
Okay, you know about that.
Here's gravity, just
default gravity.
It's going to start out
with our gravity pointing
down in our view.
Okay? Whatever down is in
our view, in other words,
increasing Y, that's where
gravity's going to start out,
we want to replace that
gravity with the real gravity
of the world, that's what we're
going to be doing in a moment.
And then here's elastic.
Elastic is an item behavior
which just sets one thing,
which is the elasticity, okay?
This says how elastic the
collisions are, and I'm going
to have it be 1.0, which
means fully elastic collision.
You can actually make this
greater than 1, and it will pick
up speed when it
hits a collision,
and you can make it less than
1, and it will dampen, right?
All the way down to zero,
which is the default, I think,
which is that's a
block, hit the bottom
and then just kind
of settle real quick.
That's what we had, I think,
the elasticity of our blocks
in Drop It, were that.
Alright, so I have this
animator and all these things,
so now I just want to make
sure that I take my red block
and add it to all
these, so I'm going
to say I want the red block
to be in the collider,
and I want the red
block to be elastic.
And I want the red block
to be affected by gravity.
Okay, so I'm just adding the
red block to these behaviors.
Again, hopefully this
is all really familiar
to you from assignment 4.
So let's run to see
what we've got now.
So we've got gravity, collision
and elastic, so this red block,
boom, it goes down
and it bounces.
Okay? So this is excellent,
we've got a good start here.
Unfortunately, this is
not real-world gravity,
I'm moving this thing around,
it's still going straight
down and bouncing.
That's because the
default gravity is down,
in other words increasing
Y in my view.
So what I'm going to do now
is use the motion manager
to set the gravity to
instead of being down,
to be wherever the real gravity
is, by using the accelerometer.
Okay, remember,
the accelerometer is always
measuring acceleration due
to gravity so I will always
know where real gravity is.
So let's do that.
That's pretty straightforward
too, we're going to d
that here in Start Game.
So to do anything with core
motion, I need a motion manager.
So let's go get a
motion manager here.
I'm going to make a
property, non-atomic strong,
CMMotionManager, okay
I need to import.
Core motion.
Oops, not core media,
core motion.
Alright. And now I have a
motion manager, and I'm going
to lazily instantiate
this thing.
So let's do that down here.
CMMotionManager, if
not motion manager,
then motion manager equals
CMMotionManager alloc init,
and then I'm going to
set the motion manager
to do accelerometer
updates at 10 per second.
Should be enough.
I mean, I'm doing UI here.
I think 10 per second is going
to give me smooth enough
changing of direction,
of my red square as I
move around, but you know,
I could crank it up a little bit
if it doesn't seem
responsive enough to my moving,
or I could crank it down if
it seems plenty responsive
and I just don't want to do any
more drawing than I have to do.
Okay? So we're going
to set it to be that,
and then let's return
our motion manager.
Okay, so now we have this motion
manager, so now what I'm going
to do is say first of all
I'm going to check to see
if I'm already doing
accelerometer updates,
so I'm going to say
accelerometer active.
Whoops! And I'm only going to
start monitoring this thing
if the accelerometer
is not already active.
Whoops, let me...erase...thank
you...okay.
So if it's not active,
then I'm going
to basically give it a block to
call me, and all I'm going to do
in that block is update my
gravity behavior's gravity.
Okay? So let's do that.
Self dot motion manager, start
accelerometer updates to queue,
so we'll give it a queue,
I'm only doing 10 per second,
I think I can easily go
main queue on this one.
So I'll go main queue,
whoops...and here's the handler.
Okay? Double clicking
to get the handler,
we'll put this right here, in
fact, we'll make it even easier
to see, we'll move this
back here like this.
Alright. So here's our handler.
And what do we want to
do inside this handler?
Well let's get the X
and Y of acceleration,
so let's get the X, that's
accelerometer data, okay?
So that's this argument here
to our block, dot acceleration,
that's the acceleration
that is happening,
and X is the direction I want.
So here's the X direction,
and then I'm going
to do the same thing Y
equals accelerometer data,
acceleration, dot Y.
So now I have the X
and Y acceleration
due to gravity.
Okay? And do the device moving.
Okay? Both.
So it's a kind of combination.
So that way, if I
tilt my thing down,
my blocks are going to go down.
If I tilt it back the other way,
it's going to go the
other way, right?
If I shake it over, it's going
to accelerate a little bit
in that direction, okay?
So it's kind of like that
block is going to be responding
to my acceleration
and my device,
but mostly acceleration
due to gravity.
So how do I set the direction
of the gravity of that behavior?
Well, I have self dot gravity,
that's my gravity behavior.
It has something called
gravity direction,
which is exactly what we want.
It's a vector, a CG vector,
and it just has a delta X
and delta Y vector, right?
So like in math, vector,
and so we're just going
to have the vector,
let's try doing X and Y,
see what happens here.
Okay? It's not going to
work, but we'll try it.
And we can do semicolon.
Okay. Now, hopefully in some
of your minds you're trying
to think why this is not going
to work, because it seems
like this should just work.
But let's see what happens.
Anyone want to hazard a guess
why it's not going to work?
Yeah?
>> Uh the vertical axis
is going to be flipped.
>> Yeah, so the answer
that was posited is
that the vertical axis can
be flipped, and that's true.
Maybe a better way of saying
it is that the acceleration
of this device is
always the acceleration
where it's...down this
way in the device,
so if I have it turned
sideways, okay,
then look at my thing
it's bouncing.
I have this turned up, face-up,
and it's bouncing this way.
Okay? Why is it bouncing
this way?
That's because it's measuring
this stuff all on this axis.
I want it measuring it in this
axis, basically, where here's Z,
here's Y, and here's X. So
I basically have to know
which way the user
has my device turned.
Okay? So I have it turned
kind of landscape, okay?
Landscape left, the home
button is on the left.
So that's going to-- I'm going
to have to flip the X and Y
and go negative Y instead of Y,
and if I were doing it this way,
which I have my...locked
in here.
But if I do it the other way
around, then it's flipped.
It has to be the other way, and
if I'm doing it up this way,
then it's another way.
Okay? So basically, which
way the acceleration
to gravity is affecting
my view depends
on what the orientation
of my view is.
So let's put some code
in to deal with that.
Turns out to be pretty
straightforward.
So we can't just
do this simple one.
So let's take that out.
And...to save some typing,
we'll do it this way.
So all view controllers have
this property called self
interface orientation that
will tell you whether you're
portrait, portrait upside-down,
landscape left or
landscape right.
Okay? That's telling
you, the view controller,
you can also get
this from the device.
And you can also get
an interesting one
from UI application, might
even be better to use here,
which is status bar orientation.
That tells you where
the status bar is.
And that's really probably
the most correct thing,
and probably what we
should be using here.
Because from the
user's standpoint,
wherever their status bar is,
that's the top of the screen.
Okay? Even if it's been
slow to notice rotation,
and it's in the wrong place, to
the user, it's still to them,
they think that's the
top of their screen.
But this is mostly going
to track that anyway,
so we'll use this
one, it's convenient.
And so here, landscape right,
you can see we are swapping
the X and Y. You see?
This is the X, and I'm
using the Y. Alright?
And so both of the
landscapes we're doing that in,
and whether Y is negative or
positive depends, you know,
Y increases as we go
down, so it just depends
on whether we're portrait upside
down or portrait right side up,
and same thing for landscape.
So you can look at these,
stare at these later,
as in convince yourself these
are the right Xs and Ys.
But you can just see, that we're
using different ones depending.
So let's see how this works.
Let's get ourselves into
a nice orientation here.
Just, here.
Alright, so now it's bouncing
around, and if I tilt it, see,
it's going off to the side, or
I tilt it to the other side,
or tilt it this way or down.
So now it's following me,
however I tilt my device,
it is following that gravity,
and I can kind of use it
to accelerate faster and
faster even, see that?
Okay? So this is
exactly what I want.
I've got this thing
so that I can control
where my red block goes,
depending on how
I tilt my device.
Okay, now I've locked my user
interface orientation here.
Watch, if I unlock it, then
it's a little disconcerting
because okay I'm clicking
here, now I turn, and whoop!
My status bar moved, but
it's still working, okay?
Because every time
I get an update,
it's still readjusting
to where down is.
Okay? It's a little
disconcerting to the user.
If I were a user and
really playing this game,
I would probably, you know, lock
my thing into some orientation,
where's that switch,
here it is, like this.
It would just be a little
more fun to play this game
if it wasn't constantly
switching my status bar,
but it's still working,
it's just a little...okay,
this is better.
Make sense what we're
doing so far?
Question?
>> Can you programmatically
do rotation lock in your app?
>> The question is can
you programmatically do
rotation lock?
Well, you kind of can,
because you can specify
that you're application
only works
in certain orientations, right?
And so by doing so, then you can
basically do orientation lock.
So. I don't know that there's
a way to kind of say "lock it
in the current orientation,"
like I'm not sure there's a way
to do that, there might be,
but I don't know offhand.
Alright. So now we've got this
thing bouncing and responding
to our thing, that's
a good idea.
One thing I'm going to
do also here is I'm going
to set the gravity
direction before we start,
equal to vector makes
zero, zero.
That's because I'm going to be
starting this thing going off.
I don't want, before the first
one goes off, to have any kind
of downward acceleration, so
I'm basically going to set it
so we have no gravity,
and then I'm going
to start this accelerometer
to get the, this going.
So that's good.
Alright. This would be kind of
cool if we added another block.
So let's put another
block in here.
I'm going to create
a black block.
So I'm going to go up here,
and set property, non-atomic,
actually might as
well make these weak,
if they leave the
view hierarchy,
then we'll clean them up.
Black block.
Notice I made these
behaviors all weak as well.
Why did I make these weak?
Well, because if this
dynamic animator isn't holding
on strongly to them,
then I don't want them.
Okay? Same thing when we
make an outlet weak, right?
If the view hierarchy
isn't holding on to them,
and since I'm not
doing storyboards here,
I'm doing code, I'm still
kind of doing the same thing.
I don't want my red
and black block to be
around if I take them out
of the view hierarchy.
Alright, so let's
make my black block.
Actually it's very similar.
Let's copy and paste my
red block, black block,
and we'll do this,
and...whoops...oh.
Well that's interesting.
Okay. That's very strange.
Okay, black block, and then
here's another black block,
and the black block wants
to be black, and we're going
to have the black block do the
collider, but I'm not going
to have it be as
elastic, nor am I going
to have it do the gravity.
So the black block, the
only thing that's going
to make the black block move is
if we hit it with the red block.
Okay? That's the only
thing that can impart.
Or if it collides with the
edge, we'll get a little bit
of reactive thing, but
that's basically it.
This is black.
Alright? So let's put
that black block on there,
and let's not have them
both start in the center,
so we'll have that
one start at minus 100
and this one start at plus 100.
So we'll see what
this looks like.
You add a black block
to our red block.
Okay. So now if I can
hit it, see you can see
that they're both
bouncing around.
The red one has more elasticity,
so it bounces off quite
a bit more powerfully
than the black block, okay?
But I can still try and get
that black block moving.
So now you can imagine turning
this into a game pretty quick,
which is the object
of the game is to keep
that black block moving, okay?
The more you can make
that black block move,
the more points you get.
So let's go add some score.
Let's add a score
to this game, okay?
Now this part is not really that
much of a learning exercise,
so I'm going to just
type that in, real fast,
you can go look at this later.
It's got a whole bunch
of properties involved
in keeping score, like
that, and all we need to do
in our code is update the score.
And I'm going to update my score
when the accelerometer goes off.
That's 10 times a second, that
seems like a good rate to me,
so I'm just going to
update my score there.
So let's look at that.
All the scorekeeping
does is just keeps track
of where the black block is,
how long it's been doing that,
and then it puts the
score in the middle there.
You can see, and it keeps
both the current score
and your highest score so
far, with this block, so.
If you let the black block stay
there, and you don't hit it,
you can see that
my score is going
to start going down pretty soon.
See, it's going down.
But if I start hitting it
again, make it move there we go,
it will start going
back up again.
Okay? And the longer I keep
doing this, the harder it is
to get my score high,
but it's doing okay.
Okay? So you can try and
see what's the highest
possible score.
Now I haven't done a very
good job here of keeping
that black block moving,
so you can imagine the scores
could be much, much higher.
Notice also for fun, I made it
so that the score bounces off.
Okay? So it doesn't
go over the scores.
This will just kind of make
it a little harder, actually,
so you can't go straight to
that block, you have to think
about the score being
in the way.
So that's kind of cool.
We're almost ready to ship
this on the app store.
What else are we going to do?
Let's go ahead and think about
an important part of a game
like this, which is pausing.
Okay? Why would you ever
want to pause this game?
A lot of reasons.
What if this game were
in a suite of games,
where there was a tab bar,
and you were switching
between games.
When you switch to another
game on a different tab,
like machismo or something, you
would want this game to pause,
and then when you went back to
it, you would want to continue.
Or what if I switch
to another app?
I go like this, and go
switch to another app
and doing some other things,
you know, and then I come back
to my game, it's like, "Oh!
My score! Ruined."
Because it was sitting there
doing nothing for all that time.
I want it to pause, and then
continue when I come back.
Or maybe I want to be able
to just tap on it and pause,
and tap again to
continue, right?
So pausing/resuming
would be really cool.
So how can we make our
game pause and resume?
Well, we can take this start
game and turn it pretty easily
into resume game, and all we
need to do to do that is check
to see if the game
is already started,
because if it's already started,
well that's going to be not,
if not red block, if we have
them start a game, then we need
to create the red
and black block.
But otherwise, we can just
fire up this accelerometer,
and what we'll do to pause
is we'll just stop the
accelerometer, okay?
So I'm just going to say
self dot motion manager stop
accelerometer updates.
Okay? So I've paused, I've
just stopped the accelerometer
from updating.
Okay? Which is a
pretty good thing.
I also have pause scoring okay,
because I want to do that.
Don't look too closely behind
the curtain for that one
and then also I want
my gravity direction
to be back to being zero.
Okay? I don't want my red thing
to be trying to do gravity
when it's paused, right?
So that's all I really need
to do to pause this game.
So let's put a little tap
gesture in that pauses the game,
and then continues the game.
Okay? This will, now can
be resume, and so I'm going
to do a tap gesture, I'm
going to do it in ViewDidLoad.
Told you I wasn't going to do
anything in the storyboard,
and so I'm just going
to say self dot view add gesture
recognizer, UI tap gesture,
alloc init with target
self, action is going
to be tap, okay, that's it.
So now I've added this tap
gesture, so now let's make tap.
And all tap needs to do is
when you say if we're paused,
then resume, otherwise, pause.
Okay? So we need an "is paused,"
how are we going to tell
if we're paused or not?
Real easy, is paused,
I'm just going to return
if the motion manager's
accelerometer is not active.
Okay? So if the accelerometer
in the motion manager is not
active, then we must be paused.
Okay?
See what I did there?
So let's go try this,
see if this works.
And this is going to work,
but not quite exactly how we
might want, as you'll see.
Alright, so let's go get some
motion happening here first.
Alright, so we got this thing,
motion, I'm going to hit pause.
Okay, so it paused,
okay, but it's still--
it paused in that my
accelerometer is not working,
but the red block still
kind of kept moving,
so here I turn it back on,
gravity's back, I pause,
it's like the red block
keeps moving a long way.
Okay, why does that
red block keep moving?
Well, because it already
has a force applied to it,
and it's just letting that
force kind of play out.
So what would be really
nice when we pause is
if we put some quicksand in
here, and everything kind
of slowed down, right?
I don't want everything
to stop immediately,
but I want things to, you
know, nicely slow down.
Okay? So how do we
add quicksand?
Well, I can do that with
another dynamic animator thing.
Let's put this down here,
I'm going to call it
quicksand, in fact, alright?
And so quicksand is
just another behavior
that I'm adding right here.
And what does this
quicksand look like?
It's this, it's what I added.
And as a UI dynamic
animator that has resistance,
so resistance is a
item's resistance
to forces being applied to it.
So I'll start it out at zero,
which means no resistance to it.
That's the default.
So it doesn't resist any of the
forces, you know, the gravity,
it doesn't resist any of that,
just things are bouncing around.
And then when we pause, I'm
going to crank this resistance
up to like at least 1.
1 is it completely resists
forces so it will slow
down really quickly, but
I can go even more than 1,
which is it actively, you
know, reduces its forces on it,
its response to forces on it.
But we've got this quicksand,
all we need to do is add both
the red item, the red block,
and the black block,
to the quicksand.
Oops, okay.
I missed a copy key quite a bit.
Okay black block.
So we'll put them both
in this quicksand,
and then we're just
going to, when we pause,
we're going to set the
quicksand's resistance
to let's say 10, which is pretty
high resistance, they're going
to slow down pretty
fast with that.
We could tweak that, whether
we want it to be that much.
And then when we
resume the game,
we're going to set the
quicksand's resistance back
to zero.
Okay? So let's do that.
And I'm mostly doing this so you
can see the effect of pausing,
because next we're going
to show how to do pausing
in a different circumstance.
Okay so here, let's go,
get this guy going again,
right, he's moving around.
Now, if I pause, let's see
how quickly he stopped.
He's still spinning, but
he has stopped his motion.
And if I continue, now gravity
is going to work on him again.
One thing about stopping him
so quickly, it allows you
to cheat a little bit, because
if this guy's out of control
and you're trying to get him
back in control so you can get
after the black block,
this stops him, right?
Too quick.
So it might be that having 10
be our quicksand is a little
too high.
Maybe we only want 1.
Which would mean it would
slow down kind of at a normal
or a much slower rate.
So you could play with that.
Alright, so when would
we want to do pausing.
Well, one of the real
important places to pause is
when someone clicks
to go to another app.
Okay? So I'm going to go back
to the slides, really briefly,
and show you this, and
then we'll go back.
So your applications state the
life cycle of your application,
it goes through certain states,
and one of them is it's
the active application.
And being the active application
means you're getting events,
your views are getting
events, right?
Touch events are
happening to you.
You're basically
responding to the world.
That's only happens when
you're the active application.
Now, if you're the active
application, you're bouncing,
you're playing your game,
what could cause you
to stop being active
application?
Well, obviously someone could
click on the home button and go
to another app, but
there's other things too
that could happen.
A phone call could
come in on your iPhone,
now you've stopped being the
active app, whatever the UI is
for handling a phone call.
Even notifications,
very important,
system notifications can
come up and, you know,
they gray out your app, they
put an alert view in the middle,
and some notification happened,
that could make your
app stop being active.
So it's important to know when
your app stops being active,
like Bouncer, so Bouncer
can pause the game.
So I like to think of
active, the activeness
of your application
being the pause/resume
of your application, okay?
So you find out about these,
your application delegates
have these methods.
Application did become active,
application will
resign active, right?
That's going in between
the two states,
but there's also a
radio station for it.
Okay? And that's
important to understand,
there's this radio station too,
and so you can have
view controllers
like this Bouncer guy, listen to
the radio station and find this
out and do the pausing.
So we're going to do that
in the demo in a second.
While I'm here, though,
let me talk about some other
application state things.
One of them is
background/foreground,
which is a different thing
than active/not active.
Okay? Active means
you're in the foreground,
and you're receiving events.
Okay? When someone clicks
to go on some other app,
you stop being active,
but you're not
in the background yet.
You can think of background
as kind of being shelved.
Okay? You're being
put on the shelf.
And when you're put on
the shelf, you don't run.
The only time you're ever
going to run when you're
on the shelf is a
background fetch,
which we saw a couple weeks ago,
or your a special kind of app,
like a VOIP app or a
location services app,
where you're getting locations,
then you can come off the shelf
and run for a little
bit occasionally,
but you're being shelved.
So background is shelving.
Active is just, you
might still be the app
that the user is using,
but something has happened
to interrupt that,
maybe temporarily.
So active is whether
you're receiving events.
It's kind of a pause/resume.
Background is shelved.
So before you get shelved,
you want to definitely make
sure your world's cleaned up
and everything is a nice
state, because one thing,
the shelf can sometimes
be cleaned off
and you're thrown out.
Okay? If memory became
a problem or something
like that you could just
completely never run again.
So when you get put
in the background,
you want to know
about that maybe.
But you find out when you come
back in the foreground, too.
So, you know, then you can undo.
Usually in the foreground
one, you undo what you did
in the background one.
There are some other application
delegate items of interest
that you can look up
in the documentation.
One is local notifications.
Local notifications are a way
for you to schedule something
to happen in your app at
a certain date and time,
and when that happens, if your
application is not running,
you can be launched
to handle it.
Okay? So it's a way to kind
of make sure your app
runs at a certain time.
So like the calendar app,
this is how it would make sure
that you got reminded that
you're supposed to go to class
or whatever, even
if the calendar app
is not even running.
So local notification
is something to look at.
State restoration, if
you got put on the shelf,
and then you got killed,
and the user ran you again,
you don't want your UI to come
up like in some base start UI,
you still want to come
back to where it was
when you got put on the shelf.
So state restoration is a
formalism where you keep track
of where your UI is, so when
you get put on the shelf,
if you get killed and
have to come back,
you can restore your
state to where it was.
Data protection is
an interesting one.
There are ways to
protect files that say
if this device is locked, in
other words, at the lock screen,
then you can't access this file.
But if it's unlocked,
then you can.
So it's kind of a special
kind of protection.
You can find out about
changes in that, i.e.,
when the thing is
locked and unlocked,
and also there's method
in there for finding
out currently is
it locked or not.
Then there's open URL.
Open URL is kind of a cool
thing, you can go into X code
if you go to the info tab
there in project settings.
You can define a URL, like
not http colon something
but like PhotoMania colon
something, for example,
if PhotoMania wanted to do it.
And then if other people
in other apps say open URL
PhotoMania colon slash slash,
something something something,
question mark, this, that,
all the full URL things you
can do in the world of URLs,
then PhotoMania would get
launched to go handle that URL.
Okay? And there will be
application delegate method
called, handle this URL.
So that's another thing to look
at in application delegate.
Okay. So. That's the end, that's
all I wanted to cover on that.
But now, let's go back to
Bouncer, and show using this.
I'm going to pause when I get,
stop being active, and I'm going
to un-pause when I go
back to being active.
And the way I'm going
to do that, very simple,
NS notification center, default
center, add observer for name,
and the one I want is
UIApplicationWillResignActive,
okay?
So that happens when I'm going
to resign being the
active application.
And object nil, I don't
really care who-- whoops!
Who this comes from.
It's going to come
from the application,
whatever, application object.
Queue, fine to do
it on this queue
that I'm calling this from.
And then, here's the block
I want to do, in my app,
really simple, self,
pause, game.
Okay? I'm going to resign
being the active application,
so I'm going to pause.
And similarly, can do the
same thing when I come back,
but here it's
UIApplicationDidBecomeActive,
and here I want to resume.
Okay? So let's see if this
works [background sounds].
Alright, so let's get
this doing something
so we can see some points
being scored, there we go.
Okay so we've got some points,
we're about 80, 90, 100, 110,
okay so if I pause, I'll still
be at 110-ish, see, pause again.
Okay, my score is saying the
same score, down now pretty fast
because I'm not hitting
that black block,
let's get him going again.
Get the score back up, okay now
I'm going to go to another app.
105, 113, so let's
go to another app.
Okay, maybe settings
or something like that,
and my app right now
is paused, I hope.
So let's go back and
hopefully Bouncer will be
about where it was, and it
is, it was at about 100.
Now, in fact, if we go away and
come back, if you first look,
you'll see it shows paused for
a second because it was paused.
Also, though, you'll notice that
see this is 71, 70, 69, 68, 67.
Okay? But now when I come back
it's not going to be at 66,
it's farther along, see 65.
Why is that?
Well that is because it does
get to run for a few seconds.
Okay? When I click
to go to another app,
I stay the active app
for a few seconds,
and it gets to run a little bit,
and now I'm not the active app,
so I pause, and then
we come back.
So there is a short
amount of time there,
just so you know
there's that window.
Okay? The last place
that we want
to pause and resume is here.
Okay? Here's ViewDataPeer,
we're already resuming here
because we used to be starting
and then we changed
it to resume.
Obviously in view
[tapping sounds], whoops!
View, will disappear
[tapping sounds],
we want to self pause game.
Okay? That's again if
we're in the tab bar,
click on another tab, we
want to pause and come back.
Alright? Okay, so I didn't
get to do action sheet.
I will post the code for
the action sheet thing.
All I was going to do
in the action sheet is just add
another button in PhotoMania
to do the little photo
choosing, called filter photo,
and it was going to
put up an action sheet,
and you could pick a photo like,
or a filter, like I had noir,
and chrome, and blur,
you pick one of those,
and it would apply
the filter to it,
and that's all we were
doing on the action sheet.
But it shows you how to
put up an action sheet,
load it up with some
things, and respond to it.
Okay? So I'll just
post that code today
and you'll get to see that.
Okay, that is it.
Uh, Thanksgiving next
week so I will not see you
for a week and a half.
Don't procrastinate on your
final projects everybody.
Get them going and if you have
any questions I'll be here.
Thank you.
>> Announcer: For more, please
visit us at stanford.edu.
