[ Music ]
>> Stanford University.
>> Okay. Well, welcome
to lecture 15
of CS193p for fall of 2013/14.
And our primary topic today
is going to be the map kit,
which is kind of the UI on
what we talked about on Monday.
As part of that, though,
especially in the demonstration
that I do, I'm going to show
you two things about SegwayÃ•s
that are pretty important.
One is how to fire
off a segue from code.
So far all the time that
we've made a segue go,
we just control, dragged from
something in a storyboard
to another storyboard, and then
we clicked on that something.
It would segue, so now
we're going to able
to fire off a segue
from in code.
And then I'm also going to teach
you a new kind of segue today,
which is the embed segue, and
I'm going to demo that, as well,
in a big demo that shows you all
the map stuff and manual segue
and the embedded segue.
Okay? So that's what's
on tap for today.
And, [pause], so, map kit.
So what is map kit?
Map kit is basically user
interface for locations
and its primary class is this
UI view, called an MK map view.
The whole map view stuff is
in a different framework,
called a map kit framework.
When you want to use the
map kit framework you have
to actually explicitly go
into your project settings
and set it to be linked.
Otherwise, all these classes
like MK map view won't be there,
and I'm going to show that
in the demo, how you do that.
So, inside of the map view, you
have these little pins, okay?
They look a little
red pin there,
they can be different colors.
These are called
annotation views, okay?
So every annotation
view in there, and you,
you can customize this, you
can subclass annotation view
to make it not look
like a pin if you want.
But these annotation
views are responsible
for showing some place in the
world, and they have a pin,
but then they also have this
little call out, if you click
on them, you see that little
white bar right there?
And that call out can be
customized, a little bit.
The title that's in it and
it can have a little subtitle
and also it can have left
and right accessory views,
so this one has a left accessory
view, which is an image view,
and there's a right accessory
view which is a little button
that you can click on.
So in our demo we're
going to do all of this.
Okay? And we're going
to cover all
of these various
parts, what's going on.
But the main thing to understand
is how you create a map view
and how you put these things
on the map, so map view,
just alloc init, or more often
we drag it out, from the palate,
in Xcode, and the, the
way we put the pins
on there is we add these things
called annotations, okay?
So an annotation is
really just an ID,
the response to this protocol.
See this MK annotation
protocol up here.
So any object in your entire
application that responds
to this protocol can just be
dropped onto the map view.
Okay? So what's in
this protocol?
Very simple, it's got a couple
optional methods, the title
and subtitle, so in that little
white call out that we saw,
that's going to be the
title and subtitle there.
And then it's got a very
important required property,
which is the coordinate.
So the coordinate is just
the latitude and longitude,
as specified by this CL
location coordinate, C struct.
And so any object
that can answer these,
really just this one
question of the coordinate,
but usually we want title
and subtitle 2, can be thrown
on a map, and, for example, in
the demo that we're going to do,
we're going to extend
photo mania.
I'm just going to throw
photo objects in there.
In fact, photo objects already
implement title and subtitle,
so all we really need to do is
make a photo object be an MK
annotation and respond
to that protocol,
is to implement the
required coordinate method
and that's what we're
going to do
and then we can just
throw photos on the map.
We don't have to make any other
special classes or anything,
just throw the photos
themselves up there.
When you're setting the
annotations, this property,
there's a property on MK map
view which is an NS array
of annotations, that's
all the annotations
that the map view is showing,
but that's read only properties,
so you can't set
the array that way,
you have to use these
add annotation methods
or add annotations, and
same thing removing them.
So you add and remove
them with these methods,
you don't manipulate
that array directly.
It's a pretty good
idea, if you can,
if you have the information,
to add all of your annotations
up front to the map view.
Adding an annotation is
actually pretty low overhead,
especially if that annotation
is not on screen currently.
And that's because those MK
annotation views, the little pin
that does the call out,
those things are reused
like a table views rows.
Okay? So if you have hundreds
of annotations, but only five
of them are on the screen,
you're only going to create 5
of those MK annotation views,
and as you kind of move
around in the world
and they go off-screen,
they'll get reused for new ones.
Okay? So letting the map view
all the annotations up front,
it's nice to help it manage
its internal performance,
or whatever.
So it's generally recommended,
but you don't always know,
sometimes you don't know all
the annotations up front,
it's happening in real
time or something.
So, [pause] what do
annotations look like?
They generally look like a pin,
but there's a lot of methods
that you can call in
MK annotation view
and you can also subclass
MK annotation view
to draw something different
in a pin, and this call
out is a little more difficult
to subclass and make different.
So if you can get by with just
doing this left and right call
out business, then that's
good because that's easy.
If you want to do more, then
it's kind of beyond the scope
of this class and
you're going to have
to do some searching around.
It's doable, but doing something
different than that basic call
out with title, subtitle,
left and right accessory view
is a little more difficult.
What, the contents of that call
out are kind of interesting,
when do you set those up?
Well, there is a map, okay,
the map view has a delegate
we're going to talk about,
and one of the map view delegate
methods is essentially build
this MK annotation view
for me, including the left
and right accessory views, you
know, alloc init those, as well,
and you can load those up
at the time you build it,
or you can wait until the pin
is pressed to load those up.
Now why would you want to wait
till the pin is pressed to load
up those annotation views?
Well, let's look at
this example right here.
You've got that thumb nail.
What if that thumb nail is,
requires a Flickr fetch?
Okay, well you don't
want to fetch that photo
for all the pins in the entire
world, or even all of the pins
that are visible on screen.
You only want to load that
Flickr, do that Flickr fetch,
when a pin is clicked on.
So there's this delegate method
in the map view called map view
did select annotation view,
it'll get called when you click
on a pin, that's a good place
to like do that Flickr fetch.
Okay? Load up the call out.
And we're going to
do that in the demo
and see how that works too.
So, we have these MK
annotation views that are going
to show these ID MK annotations,
how do those two get connected
up, and they get connected
up by this map view delegate
method called map view,
view for annotation, and it
returns a view for annotation.
This is almost exactly
the same as self row
and index path and table view.
Okay? So for running this path,
you give it a section and a row,
it gives you back a
UI table view cell,
which is a view to
display that row.
Same thing here.
It's going to give you an
annotation, and you're going
to give it back an MK annotation
view to draw that annotation.
Okay? Now, map view
even has this mechanism.
The DQ reusable thing,
like table view has.
Right? So you can say sender,
which is the map view there,
DQ reusable annotation
view with identifier,
you give an identifier,
and if it's got a pin view
on MK annotation view that
it got thrown off the screen
and it can reuse it, it'll bring
it in, and give it to you here.
Okay? But if it doesn't
have any,
then in the table view
case, what happens?
We, the system will
automatically duplicate
that prototype, right?
You got the prototype cell that
we set up in the storyboard,
and you'll duplicate it, but
that doesn't happen here,
because we don't get to
do that in the map view,
the map view doesn't
have all this mechanism
for prototype cells
and everything.
So, if that comes back as nil,
the DQ reusable, then we have
to alloc init the MK
annotation view ourselves.
So here, for example, I'm making
an MK pin annotation view,
which comes in iOS, it's the
thing that can draw a red pin,
I think you can do purple
and one other color,
and I just alloc init with init
with annotation,
reuse identifier.
Now the fact that I passed
that reuse identifier there
when I alloc init, it means
that if it ever gets thrown out,
it will come back
in the reuse queue.
So that's the thing that
ties it to the reuse queue,
so that's how things
go back and forth.
And then, inside there, I
might also set up my left
and right accessory views, maybe
not put the content in there,
like the image, but I might
create the UI image view, right,
and set that to be the left one
and then set the button
to be the right one.
Okay? Outside of that,
if not a view, okay,
so now I have an a
view either that I DQ'd
or that I made myself,
here's where I load
up whatever is cheap
to load up, okay?
Into that MK annotation view.
Okay? You wouldn't want to
do expensive things here,
we're going to wait
till it's clicked.
If this, if it's
something that's going
to be displayed immediately
about the pin,
like you're using a
different image than the pin,
then obviously you would
need to set that here,
you can't wait till it's
clicked on to load that up.
Okay? So what kind of
properties are there
on this MK annotation view,
the thing you're
returning from that method?
There's obviously the
annotation, there's the left
and right call out
accessory view,
you see those are just UI
views that you can set.
There's whether [inaudible],
there's even,
there's even whether
it's dragable.
If the MK pin is dragable,
then the annotation has
to not only implement
that coordinate method,
it has to implement the
set coordinate method.
Okay? Because you're going
to pick that view up, pin up,
and move it when it drops, the
annotation has to be moved,
so it has to have a
set coordinate method.
Okay? Otherwise setting dragable
here is not going to, to work.
And then the image that you
see here, this property,
that's the image that
replaces the pin.
Okay? That's instead of the pin.
That's not the image that's
in the call out, that's,
we're going to put in the
left accessory call out view,
call out accessory view.
One thing that's kind
of cool, if you set one
of the accessory views
to be a UI control,
most notably a UI button,
inherit from UI control, then,
yeah, you can put that button
in the call out accessory view
and you can set up
target action to it
and that would all be fine,
that would probably work,
but since that's so common to
do in a MK annotation view,
there's a nice map view delegate
method here called map view
annotation view call out
accessory control tapped
that will get called if
the person taps on it.
So if you put a button
in that little call out,
and someone taps on it,
this will get called,
and you will know which
annotation view was tapped on,
right, and you won't have to do
target action in that button.
So we'll do that in
the demo, as well.
So, did select annotation view.
So this is the thing that
gets called in the map view,
delegate map view, its
called map view, colon,
deselect annotation view, when
a pin is clicked on, okay,
when someone actually
clicks on that red pin.
Now, it's going to automatically
put the call out up as long
as you set the property
can show call
out in the MK annotation
view, by the way,
if you don't implement
that annotation view,
view for annotation delegate
method where you build the view,
it'll automatically create
one for you, it's just going
to be a pin, it's going to
have no left and right call
out accessory, but
it will show the call
out with the title
and subtitle, okay?
That's what you get if
you don't do anything.
We'll do that in the demo too.
So, here in did select
annotation view,
I clicked on the pin,
here's where I might say oh,
let's do that Flickr fetch.
You know, at least let's fire
it off in another thread here.
Okay? We don't even want to
waste our time firing it off,
and hopefully you're learning
this in your homework too,
you don't want to really
fetch that thumb nail
for a row that's not on screen,
it's kind of a waste
of time to do that.
Same thing here.
Until someone clicks on that
pin, we probably don't want
to fire this Flickr fetch off.
When the Flickr fetch comes
back, just like the table view,
we're going to have to
be careful to make sure
that this call out is still
associated with this annotation
and all that because
these things get reused,
just like the table
view rows get reused.
But anyway, so this is
where you could load
up your call out
accessory views.
You could also maybe segue here.
Okay? This might be a place
to segue, but you would have
to segue in code here,
and, because there's no way
in storyboard to like
control drag from the pins,
they're not on screen, until
you start adding annotations,
so here we could manual segue
and we'll do that in the demo.
[Pause] You could configure
the way the map displays.
Okay, first of all, the
map's display is just
like the maps app.
So I'm not really going to go
into all the things you can
configure about the map,
but pretty much anything
you can do in the maps app,
including the 3D and rotating
around, and zooming in and out,
all of that you can do
with the map view, okay?
Also including hybrid mode,
where it's a satellite
image overlaid with roads
and all that, that's all doable.
So you want to definitely
check out the MK map view API
to see all the incredible
amount of things you can do.
You can show the
user's current location.
Okay? So it'll go use that
core location stuff we talked
about in the last lecture,
find out where the user
currently is using GPS,
it'll show that on your map.
You can also restrict
things about it,
maybe you don't want the
user to go into 3D mode,
then you can turn
the pitch-enabled off
and it won't be able to pitch.
Okay? They pitch by doing kind
of a little two-finger gesture
there, they won't be able to do
that if you turn this off,
same thing with rotate,
maybe you'll always north up and
you don't want them to be able
to rotate around to a
different, then you can do that.
So let's talk briefly about the
3D of the maps, and a big issue
with this 3D, it's
not really 3D,
it's kind of 2 and
a half D, right?
This is a 2D map, but, you
know, it has some information
about buildings and
their 3D representations,
so kind of when you pitch
up, it can show you a bit
of a 3D representation.
The main way you're going
to interact with that
in code is you're going to want
to specify where the camera is.
Okay? Where is the camera
that's looking at the streets,
or whatever, that the
person looking at the map,
and you can specify the camera
via it, where a coordinate
or where the camera is, and
which way it's pointing,
and how much is pitch,
and what altitude it's at,
but one of the easiest ways
to do it is using this class
method here, camera looking
at center coordinate from
eye coordinate, eye altitude.
So, the eye coordinate
and the eye latitude,
that's where your camera is.
Where in the world, okay,
using the coordinates, latitude
and longitude, your camera
is how, what is the altitude
of your camera, in meters,
and then what point
are you looking at?
You see how that's a
way to set your camera?
So you can do that
to set your camera,
one thing that's really cool
about setting your camera,
when you move from one place
to another, you can kind
of move your camera up into
space and it'll animate that,
and then come back
down in the new place
and it really orients your user
where they are on the planet
as they go from thing,
from location to location.
Question? No.
Okay. [Pause] What about
the region, what about where
on the map we're, we're seeing?
Okay? So the region is
set with this property
in MK map view called region.
It's an MK coordinate region,
an MK coordinate region is
center-coordinate latitude
and longitude, and then a span.
Latitude and longitude delta,
how many degrees of longitude,
how many degrees of latitude
that tells you what's showing
in the map at any given time.
Okay? And this is writable and
readable, so you can set the map
to it and you can also
find out where it is now
after the user is done
pinching or whatever.
You can also just have the
map keep its current zoom
and just move around by
setting the center coordinate.
Okay? This is all exactly
what you would expect,
is this map class
is super powerful.
I mean, one of the more
action-packed classes in all
of iOS, which [inaudible].
And there's a ton of C
functions to convert between.
Okay, this is just a view,
map view is a UI view,
and of course you've got
map coordinates in latitude
and longitude, and then you've
got view coordinates in X and Y.
So sometimes you want to
convert between those two,
so it's got a bunch of C
functions for doing that
and a bunch of methods, as well,
so that you can kind of find
out what's going on,
where they're touching,
what's causing things,
etc. So there's a lot,
a lot in the map view there.
This map view delegate method,
there's about 20 map
view delegate methods,
I can only cover a tiny few
here, like view for annotation
and did select annotation view,
but another interesting
one is did change region.
And the reason this is
interesting, this gets called,
if the map is animating, moving
around, changing the camera
or something, when it's done,
it'll be showing a new region,
it'll send this message to you.
Why is that important?
Why would you want that?
Well, because if you're
going to do animation,
like I'm showing San Francisco
and the user now wants
to see New York, if you just
say set the coordinate New York,
it's going to go
whoosh, over to New York,
and the whole country
is going to rip
by in about half a second.
So it's not going to be much of
an animation, but if you want
to move your camera up over the
United States a few thousand
meters, and then
go back down, okay?
You'll need to know when
you get up to the top
so that you can start the
animation going down, and so,
region to animated will get
called when you get to the top,
you start the animation to
move the camera to the top,
when it gets to the top
and the region is set,
it'll call you this, and the
region will be the whole United
States, and then you can start
your animation and go back down.
Okay? And really, for nice
apps, you might go up,
down to New York, and then
drive down the street, okay?
It's kind of really
cool animation,
that each of those steps,
you want to use this to go
to the next step, not
the completion handlers
of the animation things,
this is much better,
because this is going
to have all,
it's going to do
all the rotation,
want everything settled down,
it's almost like the settle
down, the did pause we had
in the dynamic animator, okay?
So that's an important method
to know if you're going
to do good map animation.
Alright, so in addition
to the map view,
we also have searching.
Okay? So local search,
kind of like a geo coder,
but lots more powerful.
This basically lets
you have, specify,
let's your user specify
a natural language query,
like here I might create a
search request for 'Ikes', okay?
And I might search in a region
which is Stanford Campus,
and then I fire this off, of
course, this is going to go
over the network, not all this
information is on your device.
It's going to go
over the network,
it's going to do a fetch,
going to figure this out,
and it's going to return
to you and call you
to this completion handler
so you do it with start ,
ith completion, it'll call
the completion handler later,
and you're going to get an
array of MK map items, okay?
MK map items are
basically things
that describe a place
on the map.
And they have inside of them an
MK place mark, which is detail
about a place on the
map, like the postal code
or region it's in,
things like that.
MK map item is kind of cool
because it, there's a method
in there called open in map with
launch options, and if you send
that to an MK map item,
it will leave your app,
launch the maps app
and show it there.
So if you don't want to have
a map view and all that stuff,
you can still use this
local search and then switch
over to the maps app if
you want to show them
that particular location.
Yeah?
[ Inaudible Background
Question ]
Yeah, so the question is what
about the tiles of
the map itself?
All that map data?
Yeah, that's clearly not
on your phone as well,
that's downloaded too.
So when you first look
at a place on the map,
it's just going to
be like a grid,
kind of like the hollow deck
in Star Trek or whatever,
like just a grid, and then it's
going to fill in with the tiles.
Okay? And depending how
fast your network is,
that's how fast it's
going to download it.
So yeah, that's all
happening, though,
in the background,
you don't even know.
You have no idea
what's going on.
You can find out there
are delegate methods
that said got this title, you
know, it'll tell you if you want
to know, but generally
you don't care.
There's also a way
to get directions
from one place to another.
So, you just create this MK
directions thing, you're going
to specify an MK map item at
the beginning, at the end,
and it's going to, again, go
asynchronously over the network
and give you back a
list of possible roots
that you can take, and these
roots are going to come back
with text descriptions,
you know, take highway 101
for this much, or whatever, and
also they're going to come back
with the MK polylines.
An MK polyline, exactly what
you would think, it's a bunch
of lines, and this polyline,
you can see the blue line here,
or the purple lines, will, it's
basically a description of how
to draw and go from place
to place in the map,
which is really, really cool.
But it does beg the question
if I got a polyline, in hand,
how do I put it on the map?
How do I draw it on the map?
And the answer is we draw
on the map using overlays.
Okay? So overlays are
essentially descriptions
of some shape that you
want to draw on the map.
So a polyline would be one,
but you could also do circles,
you know, arbitrary
polygons, things like that,
and the way this mechanism
works is you create this shape,
like an MK polyline,
you add an overlay,
you see that add overlay, the
level there is whether it's
above the roads or just above
the, or above the labels,
which is kind of on top
of everything in the map,
so you could have the polyline
be on the roads and have things
like 280 symbol be above
it, which looks really cool.
But then, the MK map view is
going to ask you later, okay,
now I need a renderer to
draw this thing on the map.
Okay? An MK overlay renderer,
which is a little bit
like that MK annotation view,
except for an MK overlay
renderer is not a view,
it's more of a mechanism
for drawing on the map,
and then that renderer will
be used to render on the map.
Now, these renderers come with
iOS, there's a whole bunch
of them, circle renderer,
polyline renderer, polygon,
there's a tile renderer.
The tile renderer is cool
because it just renders
bit maps, okay,
and you can actually use
it to replace the data,
instead of the data,
that comes from Apple.
So if you had your own custom
map, you could have this tile
and it's a little bit
involved, because you're talking
about being able to zoom in and
out and it has an API for that,
but you could possibly
replace, or you might just want
to tile some image over a
certain place in the world.
And so all that stuff is built
in with this MK shape
stuff in, in MK overlay.
So, overlays are really pretty
cool and you can do a lot
of stuff with them,
so I encourage you
to kind of look at that.
Okay. So that's it for maps.
And I'm going to do a big
demo of all this stuff
so you can see all this stuff
in action, but before I do that,
there's another thing I'm going
to do in the demo that I want
to talk about briefly,
which is embed segues.
Okay? So, so far about segues,
you know how to do push segues,
when you're in navigation
controller, you know how
to do replace segues in a split
view, although we didn't demo
that and it's pretty rare
to actually replace the
detail or the master.
In a segue, we usually
just leave the detail there
and go look for it all the time,
like we did in photo mania.
Pop over segues, we saw
that last time, okay,
where we said we'll
do a pop over.
And here's another one
called embed segues.
And an embed segue
is really cool,
you create a UI view
inside the view hierarchy
of some view controller,
and inside that UI view,
is the self dot view of
another view controller.
Okay? So you basically take
another view controller's self
dot view and embed it as a
UI view inside another one.
Okay? So, that allows you to
build pretty complex views
and still divide up
the responsibility
for a rectangular
area into MVCs, okay?
And I'm going to
do this in the demo
so you get a little better idea
of what I'm talking about here.
Xcode makes is really,
really easy to do this.
You just go to the object
palate and you're going
to find something down
there called container view,
you drag that container
view into the view you want
and then it's going to actually
automatically create a little
view controller with a segue
to it, but you can control,
drag from this container
to any VC you want
to be embedded there.
Okay? And then once
you add control, drag,
it's just like any other segue.
It's just a segue.
It has to be prepared, right?
Because it's going
to be displaying something
in there, presumably.
But otherwise, it's
exactly the same.
And not only is that segue
exactly like any other segue,
the view, that container
you see right there,
that's just like
any other UI view.
So, in the post view controller,
you can set its dot hidden
if you want to hide it.
You can change its frame, okay,
you can animate its frame
changing, moving around.
You can do anything you
want, it's just kind
of an opaque UI view to you,
it's part of your view
hierarchy, you can take it
out of the view hierarchy,
put it back in, okay,
it's just that its contents
are going to be drawn
as the self dot view of
some other view controller.
Okay? [Pause] Yeah.
The timing [inaudible] this
little bit thing to be careful
of is the timing
of setting things,
because when does this
embed happen, okay?
When does it's prepare for
segue happen, basically?
Relative to other
things happening
in that view controller
that it's embedded in,
like that things
outlets being set,
etc. And the answer is
it happens pretty early.
Okay? Before outlet setting,
just like all prepare
for segues do.
So a lot of times
you're going to have
to do whatever code you
do in the prepare also
in your outlet setting, if
they depend on each other.
Okay? And we've already seen
this in photo mania before,
anything you do in your
prepare for segue that depends
on an outlet, you're going
to do it in prepare segue,
you might also do it
in the outlet setter.
Okay? So whichever one happens
second will do the actual work.
Does everyone understand
what I mean by that?
No? Yes? Who does understand
what I mean by that?
Nobody. Okay, well.
Okay, it's maybe a little
hard to conceptualize
without seeing it, so in the
demo I'll talk about it again
and hopefully it'll make sense.
Alright, so let's do this demo.
This is a very big demo.
I think I have enough
time to do it all.
Notes here, make sure, in
case I forget something.
Okay. So what are
we going to do?
We are going to start with
photo mania where we left off,
so here's the exact photo
mania we had before.
Where we left off.
This time we're going
to work in this iPhone,
in the iPhone storyboard
and then we'll copy it
over to the iPad storyboard,
so we're kind of going back
and forth working on each one.
Today we're going to go
work on the iPhone one.
So, what are we going
to do here?
So this, hopefully
you recognize this.
Right? This is our storyboard,
this is a bunch of
photographers.
We click on a photographer,
we get all the photos
by that photographer
currently in a table.
So what I'm going to build is
same UI here, almost exactly,
except for that instead
of a table of the photos,
I'm going to put a
map of the photos up.
Okay? With a pin for every
photo by that photographer.
So I'm going to need a photos
by photographer map view
controller, instead of a photos
by photographer core data
table view controller.
Okay, I'm still going to pull
all of this out of core data
and everything, but we're not
going to be doing a table view.
Does that make sense?
What I'm going to do?
Alright, so let's
go ahead and drag,
create that view controller.
So a map view controller
is just a view controller,
so I'm drag out a
view controller here.
We'll put it under
here because it's going
to be replacing this
guy right here,
which is the table
view controller of it.
Now, as with any view
controller, I need a class
for this, right now it's,
you know, UI view controller,
so I need a custom subclass,
let's go create that.
And that custom subclass
is going to be a regular,
old UI view controller.
Oops. UI view, view controller,
and I'm going to call it,
to be consistent, photos
by photographer map
view controller.
They're getting to be
pretty long names here, but,
I could call it map MVC,
but that's kind of weird.
So we'll call it map
view controller, alright?
So photos by photographer map
view controller, we'll put it
where we put everything,
we'll put it
in with our controllers,
right there.
We got that.
In our storyboard, let's
go ahead and set this thing
to be a map view controller.
Here it is, photos
by photographer map
view controller.
Let's build a UI for this thing.
Actually, before we do that,
I always like to do my
public API for any new class.
The public API for this
class is exactly the same
as the public API for the photos
by photographer core data
table view controller.
Okay? In fact, we just copy and
paste it here to this thing.
Alright? [Pause] Woops.
And of course we need
to be import here.
Okay? So it's the same thing.
You set this photographer
in our map view controller,
and instead of showing the
photos by that photographer
in a table, it's going to
put them in a map view.
So let's go back
to our storyboard
and build the UI for this thing.
And so, let's make a
lot more space here.
And go like this.
Get our assistant
editor up here.
Let's go to automatic mode .
Go here. So, here is the
code for our map photos
by photographer map
view controller,
we don't need any
of this business.
Clear that out.
We're going to need a map
view, so let's go ahead
and grab a map view, that is
just a view like text view
or any other view, here it
is right here, map view.
So I'm going to drag that
out, put this in here.
Maybe I want to do
its constraints,
already set to suggested
constraints.
Let's go take a look at those
and make sure it did
something sensible.
Oh. Yep, looks very sensible, so
it's going to stick to the size,
so whatever size our self
dot view is it's going
to stick to it.
Let's create an outlet
for this map view.
So I'm just control, dragging,
I'll call it map view.
Alright? So we got
an outlet there.
Notice that we have this
error and warning here.
Okay? What's the problem?
The problem is we have
to import MK map view.
So let's do that.
Import map kit slash map kit.
Now, I told you that map
kit, it's not enough just
to import map kit like you did
for everything else
we've done so far.
We have to go to our project
settings, over here, at the top,
and in our project settings,
along the top, you're going
to see this thing here
called build phases.
And the build phases specifies
what frameworks we link with.
Link binary with
libraries, okay?
So don't forget this part or
when you run your app it's going
to say oh, no such
class, MK map view.
Alright? So we're just
going to hit plus.
Now when you do that
you're going to see
that iOS 7 has a lot of
libraries and frameworks
that you can pick from here.
And luckily it lets you
search them, so I'm just going
to type map, and
here I get map kit.
So now I've included
map kit, I'm linking it
in with my application
so it'll work.
Okay?
Alright, so we've
got this map view.
One thing I want to do right
off the bat is set myself
to be this map view's
delegate, okay?
Because I'm going to use all
those map view delegate methods
we talked about in the slides.
So, I have this here.
So this is the setter
for map view
and I'm just hitting
underbar map view is map view,
and here I'm setting the map
views delegate to be myself,
of course it's warning
me, because I have
to implement MK map view
delegate, so I will do that.
MK map view delegate, and
now the warning goes away
because there are
no required methods
in the MK map view
delegate protocol.
All of them are optional.
Okay? Now, one other
thing that I probably want
to do here is what I always want
to do when I have a public API
that kind of sets my
model, which is to do things
like self dot title equals
the photographer's name,
okay, that's a nice thing.
And [pause] I also, here, every
time I set that photographer,
I kind of need to reset the
photos by photographer, right?
Because the whole point here
is that I show the photos
by photographer, if you
change the photographer,
I need to change those photos.
So, to do the photos
by photographer thing,
I am going to create a
[pause] new property.
Okay? Called photos by
photographer, so let's add that,
property, nonatomic strong, it's
just going to be an NS array,
NS array, start photos
by photographer.
[Pause] Okay, and this is
going to be of photo objects.
So let's go ahead and
import photo here too.
We're going to need that.
Okay, and, here's the getter
for photos by photographer.
Very, very simple, alright?
It's the getter, so I'm just
saying if the photographer,
photos by photographer is
not set yet, then I'm going
to go fetch them
from the database.
So I create a fetch
request, it's for photos.
I create a predicate, who took
equals self dot photographer,
and here, we're in
the table view,
I set the fetch results
controller,
here I'm actually going
to execute the fetch
request in the context.
And that returns an array, which
is this array that I'm going
to store right here, and
then we'll return it.
Now every time we
set the photographer,
I better reset this
thing to nil.
Okay? It's going to lazily do
this fetch whenever someone
actually wants it,
but I better set it
to nil every time the
photographer changes,
otherwise it's not going to
do this lazy instantiation.
This lazy instantiation is nice
because this is core data fetch,
but it's still work, and I
don't want to do this work
until someone actually
wants those photos
by the photographer mechanism.
Alright. So the other
thing we need to do is,
when our map view is set
or when our model is set,
we need to update
the annotations.
Okay? We've got all those
annotations on the map
that are going to be the
photos by photographer,
so we got to update those.
So we're going to have a
method here called update,
what'd I call it,
map view annotations.
And we're going to have call
this both from here [pause],
when we set the map
view, and also from here,
[pause] when we set our model.
Because we don't know what
order this is going to happen.
We don't know if our model
is set before our map view
or if our map view is
set before our model.
Either way, this update map
view annotations better do the
right thing.
Okay? So that's why
we're calling from both.
So, how are we going
to implement this update
map view annotations?
Well, I told you what
we're going to do,
we're going to make a photo
object be an MK annotation
so that all we need
to do to update
that map view annotations is
add the photos to the map.
Okay? So how are we
going to turn photo
into an [inaudible]
MK annotation?
And the answer is two-fold.
There's two things
we need to do.
One thing, we need to fix
our model, or our, you know,
core data database, to have
the latitude and longitude,
otherwise, if we don't
where the photo is,
we obviously can't
show it on there.
Right? So, I'm going
to add latitude,
luckily Flickr provides
this information, longitude,
and while I'm here I'm also
going to add thumb nail URL,
and I'll show you what we can
do with that in a moment too.
So a thumb nail URL is a
string, it's actually a URL,
but strings the best we can do.
Whereas the latitude and
longitude are doubles.
Okay? Double precision,
point and point numbers.
Okay? So I'm just adding
this to my database.
I'm going to go up here
and create my managed object
subclasses for photo mania,
I only change photo, so let's
just only regenerate photo.
We'll put this, same
place, as we usually do,
it's asking if I want to replace
the other ones, of course I do,
and now our photo has a
latitude and longitude.
Okay? Notice these are NS
numbers here, because everything
in the database is an object.
Of course we need to
update Flickr's loader,
our photo plus Flickr category,
right, which does the loading
of all this stuff, to
load up the latitude,
here I have a thing for that,
so here's the latitude being
loaded up, here's the longitude.
It's just looking at
the photo dictionary
for the latitude and longitude.
I actually take the double
value and then turn it
into an NS number just
in case this is a string.
Okay? Cause strings
implement double value too,
or that'll interpret
themselves as a double value,
so I'm just going
to be double safe.
I'm not quite sure what I'm
going to get back from Flickr,
whether it's a string or a
number in the [inaudible],
so I'm just being safe there.
And then here at
the thumb nail URL,
I'm just grabbing the
Flickr photo format square,
which is what you're using
for your thumb nail
also in your homework.
Okay? So now we've got our
data model being loaded
up with photos from Flickr
that have enough information.
Now we need to make photo be an
MK annotation, and to do that,
I'm going to add
another category.
Okay? I could put in
that Flickr category,
but it's not really
a Flickr thing
that it implements
MK annotation.
So I'm going to create a new
category called the annotation
category, on photo.
Okay? We'll put that, it's
still part of our model.
Here's the annotation
interface, okay?
And, and this is, is empty.
So, what am I going to do here?
Actually, all I'm going to do is
say photo is an MK annotation.
Okay? And I'll have to import.
[Inaudible] here.
Okay? So I've said that
photo is an annotation.
Now I can put photos
in that map view.
Okay? But if I look at the
implementation over here,
you're going to see
that there's a warning.
What do you think
this warning is?
Nobody? [Pause] Shirley, yes?
Yes, missing methods.
Because I said, here,
that I implement the
MK annotation protocol,
but I don't implement that
coordinate thing, so if I click
on this it says property
coordinate has to be defined.
Okay? So let's implement
coordinate here.
It's really easy to do.
We just have to return a
CL location coordinate.
I'm going to get it by
just getting my latitude
and longitude as double values.
Remember, these are NS
numbers, these are doubles.
Okay? So now I don't
have that warning.
Now a photo is an ID angle
bracket MK annotation,
and now I can just put a photo
into a map view as
an MK annotation.
So let's go back and do that.
There's my map view.
Here's the update
map view annotations.
First, I'm going to remove all
of our existing annotations.
[Pause] Okay, so I'm just
removing all the annotations
that are currently in the
map view, and then I'm going
to add annotations self
dot photos by photographer.
And I can, these
are photo stars,
but they're also ID angle
bracket MK annotations.
So I can call them here.
Okay? If we look at
the documentation
for add annotations,
you'll see it takes an array
of MK annotations,
and a photo is that.
I'm going to do another cool
thing, which is I'm going
to zoom the map in to show all
the photos by this photographer.
Okay? Now I'm doing this in an
animated way, but that just,
that's not really going to like
zoom out and then zoom back
in like I was talking about,
it's not going to zoom on over
to where it is, so it's
not great animation,
you're probably going to want
to do something much
better than this.
Okay? So that's really all
we need to make this work.
One thing, which
you've I'm sure learned,
is that we've changed
our core data base model,
so the photo mania that's
already on my device
or my simulator, I'm going
to want to delete that.
Okay? Because it's database
is now incompatible.
It doesn't have latitude
and longitude
and thumb nail URL in there.
So let's go ahead and run this.
[Pause] Hopefully our
network is working
and we can get stuff
from Flickr.
There it is, we got some stuff.
And now, what we'd like to
have happen here is we click
and instead of a table
view here, right,
we'd like to see this map.
Okay? So how do we
do that segue?
Alright, we have this
iPhone thing here.
We've got this nice map
view thing ready to go,
we want to segue to it, so
this segue that goes here,
we want it to go down here.
Really easy to do.
All we have to do is make
sure it all fits on screen,
so I can show it to you.
Here we go.
I'm just going to segue by
control, dragging from this row
in the photographer's table
view, down to our new one, okay?
It's a push, because we're
in the navigation controller,
and knows it took away this
segue because I can only segue
from this row to one
place, and now I'm segueing
to our map view down here.
Okay? So now we have this segue.
Now what about photographers
control,
core data table view controller?
It has to prepare for
segue properly, right?
So here's where it's prepare
for segue with the photos
by photographer core data
table view controller, so,
we're going to do
the same thing here
with a [pause] map
view controller.
Okay?
So here, this is almost
exactly the same code,
it's just that I'm doing photos
by photographer map view
controller instead of photos
by photographer core data
table view controller.
And we have these because we
have to import, so let's go
and import our photos
by photographer map
view controller, okay?
Hopefully it gets rid of
all our worms, that's good.
So now we have this
segue, so let's try again.
And hopefully our
new segue will work.
We got the photographers,
we click,
we got pins on the map, okay?
Showing all those photos by that
photographer, and if we click
on a pin, it's actually going to
show us the title and subtitle.
None of these have subtitles,
let's see if we can find
somebody who has subtitle.
There we go, there's one,
this person gets around,
from Scotland all the way
down to somewhere off the
coast of Africa there.
Okay? So this is kind of cool,
but we can't do our next step
of our segue, we can't segue
to showing the photo, and also,
wouldn't it be cool if we
had a little thumb nail
of the photo in here?
So that we could kind of look
around at the various
photos taken by this guy
until we find the one we
want, and then we'll click
on something over here to segue
over to our showing the photo
in its grand, all its grandeur.
Okay? So let's do that.
Let's put these accessory
views in.
Let's go back here to
our map view controller.
Some space.
Alright, so there's our
map view controller,
what we've done so far.
And now we're going to do
that map view delegate method,
this is our first one.
This is the one that returns
an MK annotation view given a
certain annotation.
Okay? So this is called map
view, view for annotation,
it's going to give an
annotation, that's going
to be one of our photos
and it's going to ask us
to return a view
for that annotation.
I think for speed here I have
something, yeah, let's do that.
Okay, so this is going to
do what we saw on the slides
where it's going
to have a reuse ID.
I like, this, by the way, is
a good code snippet to have.
You can see this is kind
of completely generic
for doing a view for
annotation and a map view.
I like to use the name
of my class sometimes
as my reuse identifier,
that's pretty unique
thing to do for reuse.
Here's the DQ.
If the DQ doesn't work, I
have to alloc init this thing,
I still wanted to show a call
out, I want it to be a pin
and then here I'm setting up the
view with anything I need to set
up here, it's just
setting the annotation.
So, inside here is where
we want to set up the left
and right accessory views.
So, let's do that.
So the left one is going
to be UI image view.
Okay? So I'm going to
create an image view here.
UI image view alloc init.
And I'm going to actually alloc
init with frame and this is one
of the places in iOS
I really don't like.
There is no way, no API, to find
out how big is that call out?
Okay? So I'd really like
to know how big that call
out so I can make my
image view be a good size
to really fit there, okay?
So this is a place
where you have to,
unfortunately, put
magic numbers.
Okay? And the magic
numbers I found
that really work is an
image view that is 46 by 46,
that seems to really fit
nicely in that left call
out accessory view, right there.
So that's kind of an
unfortunate piece of this.
So now I'm just going to
set that as the left call
out accessory view, and
then the right one is going
to be a button, I just
want to do a button,
so I think I have a
code for that one, yeah,
the disclosure button, so
that's this code right here.
So I'm just going to create a
UI button, alloc init, okay?
I'm going to set
its background image
to be this disclosure
image I have right here.
So let me go down here
to my image assets.
And let's drag this
in, like that.
Disclosure, I don't have
a high-rise version,
but that's okay.
This is kind of, this is not
a great solution, by the way,
to do an image here,
because really we want
to respect the tint color of the
map view and that arrow is blue.
So that's bad.
It'd be much better to either
draw it or if we are going
to use an image, you should
come to Friday's section
and understand how
to change the color
of an image using core image.
Okay? That's what we're talking
about in Friday's
section this week.
Little plug for that.
But anyway, for demo
purposes I'll use this thing.
Now I'm going to size to fit
this button to fit that image
that I just did, and
then I'm going to set it
as the right call out
accessory view right there.
Okay? Question?
[ Inaudible Background
Question ]
You mean is the, the,
you know, I'm not sure.
I believe it will size if
you make a big view in there,
it'll make it bigger, but it
starts to not look very good,
so you kind of want
to pick sizes
that make it stay the same size
because of the title
and subtitle.
It's a little bit, the
whole thing with the call
out thing is a little bit, kind
of, you have to tweak it pixel
by or point by point,
unfortunately.
So, let's go ahead and take
a look at what we got so far.
Alright? So now if we look at
a photo and we do the call out.
We have room for the image.
We're not putting the
image in there yet,
and we have this little button,
which when we click
on does nothing.
Okay? So we're getting closer.
So let's put this image in
there, and let's make it
so clicking on this segues.
Alright. So, how do we put
this image into the image view?
Well, I'm going to
have a little method
to do it here called,
what did I call this?
Update left call
accessory, yeah,
so here's my method
that does this.
This looks like maybe
a lot of code,
but it's actually not that much.
Mostly what I'm doing
is introspection to find
out if the left call
out accessory view is,
in fact, an image view.
Because I might have multiple
pins with different call outs.
Some of them might
be showing photos,
but some might be
showing something else.
And maybe those are
something else they don't have
in image view as a the left
call out accessory view.
Alright? So I'm both checking
to make sure that my call,
left call out is an image
view, I'm also checking
to make sure my annotation
is a photo, right,
and not something else.
Because it's perfectly
reasonable on a map
to have annotations,
some of which are photos,
some of which are
something else.
Okay? The hometown
of the photographer
or something like that.
So that's what mostly what this
is it just is introspection
of those things.
Once I have a photo
and an image view,
then all I'm doing is
this horrendous image
with image data, data
with contents URL,
and why is it horrendous?
Of course because I'm
blocking the main queue,
but this is a demo so I get
to do it, you get to do it
in your homework, but I do.
So, this is going to
block the main thread
for a short amount while
it goes out to Flickr
to fetch it, but
you get the idea.
I put the warning in,
I'm sure I'll go back
and fix it some other
day, yeah, sure.
Okay. So that's that.
Let's go ahead and look at that.
[Pause] Alright, so now if we
click on something and we click
on a pin, hopefully
we'll get an image,
maybe that guy didn't have,
oh, that's not working,
why is that not working?
Oh! I, no, that's not why.
Okay, so, oh, I see
why [laughing],
because we never called this.
Okay, it'd be nice to call that.
It would probably work better.
So let's call that from here.
Okay, this is the
view for annotation.
So I'm calling this left
call out here from view
for annotation, right, because
I'm returning the little view
for annotation, that includes
building the call out view,
so that'll work a lot
better, I'm going to guess.
Alright, so here we go here.
Click this up, nice, that
looks pretty good, right?
So if we click on each one, we
kind of get a little look at it
and it kind of helps us decide
which one we want to step on,
press on, and we'll
press on this one.
Now there's a problem with
the way I've done this though.
Let's find somebody who
has a lot of photos.
Hopefully somebody
has a lot of photos.
Somebody, well, eh, looks like
15 photos is, or 16, alright.
So let's click on this guy.
When I click on this guy,
look, it's taking a long time.
Okay? And this is a fast
Stanford network, so,
it's not too slow, but it still,
it's blocking the main thread,
but it's blocking the
main thread a lot.
And why is that?
That's because it's fetching
the thumb nail image for all 16
of those photos, okay?
All at once up front.
So we do not want to do that,
we only want to do this fetch
when you click on the pin.
So we're going to implement
another map view delegate
method, and you can see how
many methods, delegate methods,
the map view has, quite a few.
We want this one down here,
did select annotation view.
That's get called when
the pin gets clicked on,
so I'm just going to move
this update left call
out accessory view down to here.
Okay? So now, when we run,
even the 16 guy is going
to instantly come up, because
you're not doing any fetching,
but when we click on
this, little delay, okay?
Now I can't see all 16 of things
because they're all stacked up,
it's a little problem with
our UI, we probably want
to have some way to click
other things, you can kind
of click other things
one, but, anyway.
But now we want to, if I click
here, I want to see this image
in my image view
controller, full-size.
So now let's implement
this little thing.
And that it's going to
do, we're going to do
with another map
view delegate method.
This is the one I
told you about,
the call out accessory touched,
it's the one up here, see it?
Call out accessory
control tapped, okay?
So this gets called whenever any
left or right accessory view,
that is a UI control, like
UI button is, gets tapped.
This gets called.
Now I only have one because the
image view is not a UI control,
so I don't have to
worry about that.
So inside here, I
can segue, okay?
But now, how do I segue, okay?
I've never shown you how
to do this, we did cover it
in the slides, but
I never showed you,
how do I actually
segue from here?
Because if I go back to my
storyboard, there's no pins
for me to control, drag from?
How do I, what am I going to do?
Okay? So what we're going to
do here is segue from code,
and here's how you
segue from code.
You control, drag from
the from view controller
to the two-view controller, just
from the whole view controller,
not from any button in
here or anything like that,
and if you had a lot of buttons
in here, you could control,
drag from this bar so you
don't accidentally control,
drag from a button.
So I'm just going,
and I'm not control,
dragging from the map view here.
Okay? In fact, here I'm going
to control, drag from down here.
Okay? So here's my photos
by photographer, here's the,
the icon that represents the
controller itself, so I'm going
to control drag from here up to
this guy, if I can reach him.
Oh well, trust me.
>> I'm going to control
drag from here to here.
Okay? There we go.
No. This to here.
Sorry. There we go.
Alright. So I'm going
to control,
drag from this view
controller to this one.
So, I'm creating a segue here
from that view controller
to the other one,
generically, from the controller
to the other controller,
and it's very important here
I have to give it a name.
So I'm going to call
it show photo,
because that's what it
does, it shows this photo.
Okay? Why do I need
to give it a name?
Because from code, I have
to refer to this segue,
and I'm going to
refer to it by name.
Okay? We'll call it show photo.
So let's go back and do that.
This is a one-liner.
I just say self, and I'm
going to call this method
in IU view controller,
UI view controller,
called perform segue
with identifier.
And here I press,
specify show photo.
And notice I get to
specify a sender.
Okay, where does
this sender come in?
Well, this segue that I'm
going to perform, in code here,
still gets prepared for segue,
it's a normal push segue
so it has to prepare for segue
and if we remember prepare
for segue, it takes a sender.
And what is that sender?
Well, in the table
view controller case,
remember the sender
is V table view cell.
So what would we want the,
the sender to be here?
Probably this annotation view.
Okay? This annotation
view, right here,
is the annotation view that
has the call out that we tapped
that accessory view in,
so that's going to tell us
which annotation we're
talking about, right?
So that's the sender
that we're going to see
when we go do prepare
for segue for this thing.
Alright? Alright, so let's
do the prepare for segue
because this is a normal segue.
It has to be prepared, just
like all the rest of the segues
that we have, and to do that,
I'm going, just like I did
in table view, I'm, I have
a little prepare here,
which is right here,
and this prepare kind
of generally will prepare this
map view controller to segue
to an image view
controller, okay?
And it looks a lot like the
other one, okay, here I'm going
to find out if the annotation
that we're going to prepare,
so we're going to prepare for
view controller, this is going
to be an image view controller,
to do a certain segue
to show a certain annotation.
And this had better be a photo,
because that's all
I know how to do.
So I'm checking here to
make sure it's a photo.
And if it is a photo, then I'm
going to check the identifier,
same thing with table view,
if there's no identifier,
then it's the kind of class,
it's an image view
controller class,
then I'm going to segue to it.
Okay? So this, I'm going
to call this from prepare
for segue in a second.
Let me go ahead and import
image view controller.
[Pause] Okay?
Just so you don't have
the warnings there.
So, everyone understand
what this code does?
Well I do the code
significance for speed,
but I want to make sure
you understand all the code
that appeared there, okay?
Alright, so now we can
do the prepare for segue
and it's going to
be pretty easy.
So void, prepare for segue.
Okay? And in prepare
for segue, the sender is
that MK annotation view.
Alright? So we know from
that MK annotation view
which thing it is, so
I'm just going to say
if the sender is an
MK annotation view,
which it should be,
okay, then I'm going
to call this prepare
thing, that I did up here,
this prepare view controller.
Prepare view controller.
And the view controller
I'm going
to prepare is segue dot
destination view controller.
The segue is the
segue's identifier.
Oops. Identifier.
And the annotation we're
going to show is what?
The annotation is this sender,
which is an MK annotation view,
is it's annotation,
so MK annotation view has a
property called annotation,
which returns to the
ID MK annotation that,
which is we want here, that that
MK annotation view is showing.
So I'm going to say
MK annotation view,
sender, annotation.
Okay? Make sense?
[Pause] So, we make it so we
perform the segue, when we call
out accessory is tapped, prepare
for segueÃ•s going to get called
when that happens, and we're
going to call this thing
to do the actual preparation,
checking to make sure
we're talking about photos,
making sure we're segueing
into an image view controller.
Okay? Everybody got that?
Alright, so let's
see if that works.
[Pause] Okay, so
let's go to this guy.
This is our world traveler here.
Let's go click down here.
It's got this, it's got
a nice image view here.
Let's try and click on
this, and it segues.
Okay? And we'll get
the exact same kind
of segue we got before.
Make sense?
Questions about that?
Alright. So that's really
pretty awesome in, you know,
about 20 minutes, or
25 minutes we were able
to add full map capability
to our iPhone version.
But what about our
iPad, our iPad version?
Let's turn off my phone here,
and go do the iPad version.
So the iPad probably wants to
be slightly different than this.
Although I'm going to start
off having it be very similar.
What I'm going to do in the
iPad is I'm going to get rid
of this photographer's table and
photos by photographer table.
So I'm just going to delete
that, and I'm going to copy
and paste what I just
did, here, in this one.
Copy, over here, paste, okay?
Let's control, drag.
Push segue, or sorry, root view
of this split view controller.
Let's go find everything.
Make some more space.
There it is.
Okay? So now we're going
to do the exact same
thing we did before.
We got the photographers
here and we're going
to have the maps here.
So let's go see what that
looks like, at first.
[Pause] Alright.
Is this working?
Yes. [Pause] Oh, wait, we're
going to have to, sorry.
We're going to have to stop here
because we are going to crash
because we changed our
[pause] database scheme.
So let's get rid of that.
Run again.
[ Background Sounds ]
[ Inaudible Background
Question ]
Okay, so the question is
if I were in the real world
and I had apps on the app store
here and I changed my scheme
and my users upgrade,
how would I handle that?
And the answer is, and we don't
teach this because, you know,
you know how it is,
time constraints,
but there's actually a mechanism
for versioning your scheme
and then doing auto-migration
from previous versions,
getting the data out
of a previous version
to the new scheme.
So you have to change your
scheme in a compatible way,
obviously you want to keep
all your user information,
but that migration
can be automated.
So it's pretty cool actually.
So, check it out in the
core data documentation,
how to do it.
Alright, so here's our
photographers, and if I click
on a photographer, then
awesome, I get the map, okay?
And if I click on
these, woo, that work,
this is just awesome,
it works great.
Let's try and show the photo.
Uh oh. Why doesn't that work?
Because it's doing
perform segue here, okay?
And we don't use a
segue on the iPad,
we just go find the image
view controller in our detail
and update it directly, right?
So we have to do the same fix we
did when we made this work on,
with the tables, we got to do
the same thing with the map.
So, I'm going to go back, back
to my map view controller.
Right here.
And I'm going to put some, I'm
going to put a property in here
that finds an image view
controller in the detail
of a split view I'm in,
if there's one there.
Okay? Call this image
view controller.
And here's the property
I'm going to add.
Right? So this property is
going to be nil if I'm not
in a split view controller
where my detail is
in image view controller.
Right? Otherwise, it's going to
be that image view controller.
Ya'll recognize this code
from our table view, right?
Split view controller last
object, that's our detail.
If it's in a navigation
controller, I'll look in to see
if it's the root
view controller.
And then if it's
a view controller,
an image view controller,
I'll return it.
Otherwise nil.
So on the iPhone this
is going to be nil,
because it can't be a split view
on the iPhone anyway, right?
So this is cool.
So now I know whether there
is an image view controller,
so I could do something,
for example, when I'm doing
that segue, where's that segue?
Right here, this perform segue.
I could say if I have an
image view controller,
then just update it
directly, directly, otherwise,
you can try this perform segue.
By the way, there's
really no way
to try this perform
segue without crashing.
So you really got to know
whether you're actually
segueing, okay?
Luckily, here, if I have an
image view controller around,
I know I'm not going
to be segueing,
and if I don't have an image
view controller around,
I probably do want to be
segueing, because I got
to show the photos somehow, if
I'm going to allow that, the,
that little button to be there.
So if I have this self-image
view controller here,
I can just prepare it.
So we've got this
prepare controller,
I'm going to prepare the image
view controller, segue is nil.
The annotation, what, where does
the annotation want to show,
again, its right here, okay?
View dot annotation.
[Pause] Woops.
I always do that, don't I?
Okay? So there's that.
Okay, so I'm just
going to prepare
that image view controller
if it's there,
otherwise we'll segue.
Everyone understand this?
So let's go look at this.
See how this works.
[ Background Sounds ]
Alright, so let's look
at some photos again.
I'll find some photos like,
I don't know who we got here,
this guy, so we got this.
Click this, okay?
We got this right here.
If I click this little
button, it worked.
Okay? So it uses the thing, if
I go over here somewhere else,
let's do this guy right here.
Notice that overlaps,
I don't really
like that UI very much,
but it is what it is.
Okay? So that's kind of okay.
This is kind of okay,
this UI, it's really not
that great though
because I'm showing
that little thumb nail there
and then I'm, I click on it,
I'm also showing the big one.
Why don't I just not
have a thumb nail,
not have the little blue
arrow, and every time I click
on a pin, just show the photo.
There's no reason not
to just show the photo.
So how would I do that?
So just forget this whole thing
with that little blue arrow,
you don't even need
that blue arrow.
Let's do that.
Let's go up here to, this
is where we build the left
and right accessory
views, right here.
So I'm going to say if I don't
have an image view controller,
then go ahead and put
those call outs in there.
[Pause] Otherwise
don't put them there.
And then also, now that the call
outs are there, aren't there,
this is never to get called, in
here, because there's no call
out accessory if I have
the image view controller.
So let's go ahead and
cut this out of here,
and let's put that
code in select.
So here's where it's selected.
So when it's selected,
let's go ahead and [pause],
views control higher, woop,
nope, that didn't work.
[Pause] Control I, there we go.
Alright. So when the pin
is selected, when we click
on the pin, if I have an
image view control around,
I'll just prepare it.
Otherwise, I will update the
call out accessory views and all
that stuff, which I don't have
if I have an image
view controller.
So let's see what
this looks like.
[ Background Sounds ]
Okay. So, no.
Let's click again here.
And it's going to be a
multiple, oops, multiple photo.
Okay, so, let's click
on one here.
Alright, automatically,
as soon as I click on it,
it's going to update
the image view.
Okay? So that's kind of cool.
That's a nicer UI.
I don't really need
that thumb nail.
I don't need the [pause],
the other stuff on there.
So that's cool.
One thing though that's a little
weird on it is watch this.
So now if I go here, okay?
Now I've got dead man
Jones, his photos,
but I've got somebody
else's photo on the right.
That's kind of weird.
Okay, that's kind of a bad UI.
So let's make it so that
every time we show the photos
by a photographer,
let's just autopick one.
Okay? How would we do that?
Very simple.
When we update our view,
map view annotations here,
I'm just going to
autoselect a photo.
But only in the case where I
have an image view controller
lying around.
If I have an image view
controller lying around,
then I'll autoselect a photo.
And how am I autoselecting?
Well, I'm just grabbing the
first object out of our photos
by photographer array, okay?
And if there's any in there,
then I'm going to select
that annotation, okay?
That causes that call out
to appear, but it doesn't,
when you select it with
this, it doesn't call
that did select method
up here to get called.
Okay? This method, up here,
did select annotation view,
this only gets called
when the user selects it.
Okay? So the user didn't
select it here, we selected it,
so we still have to do
that prepare ourself.
Okay? Let's take a look at that.
[ Background Sounds ]
Alright. So let's
pick somebody here.
Click on that, and you can see
it autoselected the scooter one,
I picked something else, we'll
go here, it autoselects one.
It goes here and autoselect one.
So now we're kind of all in sync
between the two, between what's
on the right and
what's on the left.
Okay? But if I click
another one, it'll change it.
Okay? So that's not a bad UI.
I kind of, kind of like it,
it's kind of okay, but I'm going
to show you another way
to do this on the iPad,
to show you the embed segues.
One could argue whether this is
a better UI or the embedded one,
you can kind of see them both
and you can tell me what
you think is better.
But what I'm going to do is,
you see where the sun is
right there, in that photo?
I'm actually going to take this
map view that's on the left,
and I'm going to embed it
inside this image view.
So this map view is
going to showing there,
and so what we're going to see
in this column is just
the photographers,
these will be photographers.
Embedded where the
sun is will be the map
for whatever photographer
we choose
and then we'll have our photo,
and we'll still be able to zoom
in our photos, zoom around,
it'll be occluded, right?
It'll be covered up, although
maybe we could add a button
somewhere that would hide
that map and reshow it,
a little bar next to the
URL, because that map,
it's just going to be a UI
view, in there, we can hide,
set it to dot hidden, we could
animate it, transparency,
fading away, whatever
we wanted to do
because it's just
going to be UI view.
But the contents of
that UI view are going
to be the same contents
that are in this map view
over here, exactly the same.
I'm going to use that
exact same view controller.
Okay? And this is,
again, we're talking
about view controller reuse.
Okay? So, let's go
ahead and do that.
By the way, we noticed we have
warnings back in the code here.
Why do we have this warning?
That's because autoselected
photo is a photo star,
and we don't import anything
here that tells this code
that a photo star
is an MK annotation.
Okay? So we have to go up here
to where we import a photo,
and instead import photo plus
MK, or sorry, plus annotation.
Okay? Because this is
where, this is the file
in which we declared that
photos were MK annotations.
Everyone understand that?
Okay, so you got to make sure
you import whichever interface
defines that.
Okay. So how are we going
to do this embed thing?
Let's go over here to our iPad,
storyboard, all I'm going to do
to start is to drag an embedded
thing in here, so let's do that.
This is a bit of a
challenge when it comes
to screen real estates.
Let's go here.
So the embedded thing, it's
down towards the bottom,
here it is right here.
You see it says container view
defines a region of view control
that can include a
child view controller.
Okay? So I'm going to drag
that into this view
controller right here,
this is our image
view controller,
so let's drag that in.
Now when you drag this in, it's
a bit of a pain in the neck
to size this to where you
want and all those things,
but I'll show you another
way, when you have a big view
like that, that you're
dragging in and you want
to place it somewhere to do it,
which is use the
document online.
So here's the document
outline, here's my container.
Notice it added it as a
subview of my scroll view,
but I don't want to scroll that
map around, I want it to sit
on top of the scroll view, I
actually want the scroll view
in the back, and then
the container view,
and then we can do our
activity indicator.
So that's one thing
I can do is move it
out from being a scroll view.
And can anyone think of a way
that I can place this
container where I want?
Magic word, begins with an A?
[Pause] Down here?
Yes, we can use autolayout.
Right? To specify
exactly what we want.
So let's fix its
width and height.
This is a case where we would
want some magic numbers of width
and height, because we want
to kind of decide how much
of that view we want to
occlude with the map,
what the relative value is,
and then let's also pin it
to the top, by the standard
value, and then let's pin it
over here to the right,
so we're going to put it
in the upper right-hand corner.
And let's see what that
looks like, first of all,
and notice we have the
little yellow thing here.
Because we have a misplaced
view, and so I'm going
to go ahead and fix
that misplacement.
And now let's go look and
see how this looks like.
Eh, it just doesn't quite
look like I want, because it's
under this bar, I don't
really want it under this bar,
so it's a standard width from
the top, but under this bar.
The way you can, by the way,
control whether things are
under the bar is select your
whole controller and inspect it.
And you'll see down here,
there's extend edges, okay?
So if I say under top bars, no,
then it moves that
container down.
Now that created some
autolayout things,
both for this little spinner guy
and this, so let's fix those.
Right? Because we moved
everything down a little bit,
so those need to be
put back relatively
to where they should be.
So that's a way to do it.
Now, of course, now the image
view is not under there,
and I might want one
and not the other,
and we can certainly set
our autolayouts constraints
to whatever we want,
but I just wanted
to show you this
extend edges thing.
Some of you discovered this
when you were doing your
homework before, but, so, okay.
One thing that happened
through all of this,
we got this container, is
we have to specify what's
in this container, and it
actually put something on here.
Okay? It's hard to see,
let's move it down.
It actually put this, this is
a generic UI view controller,
if we inspect it, you can
see it's a generic view I
view controller.
So this view controller,
self dot view,
is going to appear in here.
But I don't want
this view controller,
self dot view, in here.
So I'm going to delete that.
Instead, I want this view
controller's self dot view,
the map view controller, right?
So how do I do that?
This is a segue, control, drag.
So I'm control, dragging
from the container to here,
only option I'm going to have
is embed, because the source
of this is the container, and
now I've embedded that in there.
Okay? I don't want
this segue anymore,
because I don't want when,
I choose the photographer,
I don't want to load
this table up,
so let's get rid of that, okay?
And so now, we have this here,
let's move it down again.
Okay?
So this photos by
photographer map view is going
to appear inside here.
Now this is a segue, okay, maybe
we would call this embed map
or something like that, because
that's what this segue does.
This segue has to be prepared,
just like any other segue,
this is a normal segue.
Who's going to do the preparing?
Well, it's the view controller
into which this is embedded.
That is this.
Which is an image
view controller.
How does this photos
by photographer need
to be prepared?
It needs the photographer,
right?
That's its public API, just
to remind you, over here,
photos by photographer
map view controller,
needs a photographer.
So if we're going to prepare it,
we need to pass the
photographer.
Well, that's a problem, because
this image view doesn't have
the photographer.
All the image view
has is the image.
Okay? So we're going to have
to create a little subclass,
of image view controller,
which I'm going to call photos
by photographer, image view
controller, and it's going
to pass the photographer
on through.
Okay? So we're going
to segue to it,
like we are already segueing,
it's not really segueing,
but we're setting this
detail view controller,
we're preparing already,
but we're going to have
to prepare it with
this photographers.
So let's create that
subclass with this controller.
Okay. It's a subclass of
image view controller,
we're going to call it photos
by photographer image
view controller.
Okay. We'll put it
with our controllers.
Okay? Now in the iPad only,
I'm going to change this
to not be an image view
controller anymore,
I'm going to change
it to be a photos
by photographer image
view controller, okay?
So now it's a photo
by photographer image view
controller, which is this thing.
The public API of this,
again, it's the same
as all these other photos
by photographer, it's this,
actually watch this, we'll copy
this, and we'll paste that,
put that there, put that
there, get rid of that,
see it going back
and forth too much.
So it has a photographer,
it's a photos by photographer,
but this is an image
view controller,
so it has a photographer.
What does it do when it
gets the photographer?
Well let's look at that.
In fact, before we
even look at that,
let's talk about
that embed segue and,
and having it prepare.
So I got that code here
so we can look at it.
Okay? I'm going to put this
up here, talk about all this
in a second here, and I'm
going to import photos
by photographer map
view controller.
Alright, so let's look at
this code that comes in.
So this is photos
by photographer image
view controller.
It is, right now, trying to
handle preparing that embed.
So it's preparing
this segue right here.
Its doing the prepare
on this segue.
Okay? How is it doing that?
Well, it's looking to see
if the destination view
controller is a photos
by photographer map view
controller, which it should be,
that's what we're embedding.
Right? Photos by photographer
map view controller,
and if it is, then it's going
to set the photographer.
Completely obvious, right?
And it's setting it to
self dot photographer,
that's this method
right here in photos
by photographer image
view controller.
That's all good.
But notice also I'm grabbing
a hold of this photos
by photographer map control.
Why am I grabbing a
hold of this thing?
Well I'm grabbing a hold of this
because this might get called
before [pause] this gets called.
Okay? In other words, before
my image view controllers,
photos by photographer
image view controller,
gets it photographer, it
might be asked to segue
to the embedded one,
and it might not have
this photographer yet.
So we need to go here,
in our set photographer,
which we always do this anyway,
things like self dot title
equals photographer dot name
and stuff like that.
We, here, we need to
[pause], if we get here
after we've embedded,
then we need
to say self dot map view
controller photographer equals
our photographer.
Okay? So this is the exact
same thing we're talking
about for prepare can
happen before the setting,
setting can happen after,
you know, back and forth, so,
but I have to do it in
both of these places.
Does that make sense?
Okay? Good, now I'm seeing
more nodding heads this time
and that's good.
Because when I talked about it
in the slides you were like huh?
Okay, but now you understood.
If we set our model after we
are asked to prepare that embed,
then we have to go back
and set the photographer.
Okay? Now the last thing we need
to do is do the preparing
of this guy now.
This guys needs to be prepared,
it's the detail view controller,
right, of this split view.
And so now when we click in
the photographer's thing,
we need to prepare this guy by
passing him the photographers.
So that's really easy too.
Here's where we prepare, right?
So right now, in our
photographers table view,
we know how to prepare
the photos
by photographer core
data table view.
We know how to prepare
the photos
by photographer map view
controller, and now we know how
to prepare the photos
by photographer image
view controller, as well.
Okay? We prepare them all
exactly the same way, right?
But they're all different
destination view controllers.
So let's import,
make that go away,
photos by photographer
image view controller,
and now we can run.
[ Background Sounds ]
Okay. So you can see our map,
comes up the United States,
we haven't chosen a
photographer, so there's,
there's no photos in there.
Let's pick a photographer.
This guy. Okay?
Here's our map, we can kind of
see where in the world we are.
Looks like we are
in Taipei, okay?
And it's showing
the scooter thing.
If I click on the other
one, we get that one.
Okay? So this is quite
a different kind of UI,
I can still zoom out here, I
can still move this map around,
I could rotate it
around, I can zoom in.
Alright? I can click on
another photographer here.
Right? [Pause] Okay?
Make sense?
How that's working?
So, I mean, again, you can
argue which is the better UI.
This one's kind of cool.
I think if I had a little button
up next to URL that said map,
that was maybe a little
map icon and I pressed it
and the map would disappear and
reappear, this might be kind
of cooler because if you look at
this UI, I have my photographers
and my photos and the
selected photo all
on screen at the same time.
Alright? So I can be zooming
in, looking at anything I want,
clicking on another
photo by this guy,
switching to another
photographer, back and forth,
all, right at my fingertips so
that I don't have to, you know,
be segueing and things
changing and all that.
Make sense?
Okay. That's all I
have for you today,
and I will see you on Monday.
Good luck with your
homework that's due on Friday
and I'll be here for
any questions you have.
>> For more, please
visit us at stanford.edu.
