[MUSIC]                         
Stanford University.            
>> Okay, well,                  
welcome to Lecture 9 of         
Stanford CS193P, fall of 2017.  
So we have two                  
major topics today.             
We're gonna talk about          
the view controller lifecycle,  
which is when your              
MVC comes alive and             
does all the things it's gonna  
do and then dies eventually.    
And then we're also gonna talk  
about a UIView called scroll    
view which allows you to look   
at large UIViews by panning     
around on them, zooming in      
on them, etc., all right.       
View controller lifecycle.      
So the view controllers         
have a lifecycle.               
It's essentially marked by      
a sequence of messages.         
Methods that are invoked        
on the UIView Controller.       
Why do we care about this at    
all? Because at the various     
stages of the lifecycle of      
the view controller we might    
wanna get involved, right,      
and do certain things. So       
this lifecycle starts with      
the creation of your UIView     
controller. For your view       
controllers that you write,     
this is almost always           
coming out of a storyboard,     
right? You drag something into  
storyboard, you put your views  
in there. That's how you        
know how to create an MVC.      
So your lifecycle is gonna      
be when your app runs and       
you've segued to something or   
it's your first view            
controller in your storyboard   
or what whatever. Now iOS       
also has some API which we      
haven't learned about yet       
that can hand you a view        
controller, that you can        
then segue to or whatever.      
For example, there's a view     
controller that lets you take   
a picture with the camera.      
So, it can come from code as    
well, it's not only out of      
the storyboard.                 
But once it's created,          
what happens to it? And you've  
already actually learned a lot  
about the lifecycle             
of a view controller.           
Of course, the first thing      
happens after it's created is   
that it gets prepared to be     
segued to, right? So if it's    
being put on screen because of  
segue, the preparation          
part happens.                   
Then after that importantly,    
the outlets get set, or         
your UI buttons and whatever    
those connections get made.     
Then your view                  
controller appears and          
possibly disappears on screen,  
right? Imagine a split view in  
portrait mode, the master       
disappears. And then you can    
slide it out and then slide     
it away so it's appearing and   
disappearing, for example.      
Also, along the way, as all of  
this is happening you might     
have geometry changes.          
Most notably,                   
you rotate your device, and     
you go from having this tall,   
thin rectangle to a shorter,    
wider rectangle. So             
geometry changes can happen.    
They can happen for             
other reasons, as well.         
Again, in a split view,         
you know, your master,          
sometimes it's in the little    
tall thing on the left.         
Sometimes there's a shorter     
thing on the left and           
the one thing on the right,     
it sometimes fills              
the whole screen.               
Or, when it's in landscape,     
it fills just a part            
of the screen.                  
So the geometry is changing,    
depending on the situation      
of your view controller.        
And then lastly, and            
by far least importantly,       
in a low-memory situations,     
you might have your view        
controller asked to free up     
some memory, we'll talk         
about that in a second.         
All right, so let's talk about  
all the methods that get sent   
to your view controller when    
all these things happen.        
Now, you already know           
the first one, viewDidLoad, I   
mentioned it in your homework.  
It's a really powerful place    
to do initialization, because   
you're already prepared and     
all your outlets are set, so    
now you really can go to town.  
So it's great for that and      
we usually put most             
of our initialization in        
viewDidLoad with one huge       
exception. And it's in red so   
don't miss it geometry.         
When viewDidLoad runs, your     
bounds have not been set yet,   
so do not put things in here    
that have to do with the size   
you are on screen. Because      
you have not been fitted to     
whatever device you're on,      
okay, it's very important.      
Very common mistake to put      
stuff in here and then you      
just wonder why, when you run   
it on a different device, your  
code doesn't work anymore.      
So do not put geometry.         
I'm gonna show you where        
to put your geometry            
changes in a moment here.       
So that's viewDidLoad,          
a great one. The next           
one is viewWillAppear.          
Now by the way, notice in all   
of these I'm calling super      
viewWillAppear or super         
viewDidLoad in all of them,     
you always do that,             
do not forget to do that.       
In fact in a demo the other     
day, I noticed I forgot to do   
it and so we'll fix             
that in today's demo.           
But, and                        
I noticed in your homework,     
many of you did not call super  
viewDidLoad. Now of course,     
we didn't talk about            
view control lifecycle so       
I don't blame you for that.     
But from now on,                
don't forget to do super,       
give your super class a chance  
to find out it's appearing or   
that it got loaded or           
whatever. So viewWillAppear,    
what do you do in               
viewWillAppear?                 
viewWillAppear is exactly       
what it sounds like,            
your view controller's view     
is about to appear on screen.   
So this is where you can catch  
up with stuff that might've     
gone on in the world while      
you were not on screen. Or      
if this is the first time       
your coming on screen,          
catch up with the state         
of the world. So                
this is really a place to kind  
of load up your view with all   
the information from your       
model, especially if your       
model can change like let's     
say your model is a network     
database. Other people are      
editing it, so it can change.   
So here's where you wanna get   
up to date, because when your   
view is not on screen,          
you don't want your view        
controller to be doing much     
work. It just wants to kinda    
sit there quietly, and then     
when it's time to come back,    
then it does its                
little setup in here.           
One thing to notice about       
viewWillAppear of course,       
it can be called repeatedly,    
because your view controller    
can go away and come back,      
and go away and come back.      
But viewDidLoad is only ever    
called once in the lifetime     
of your view controller. It's   
just called once after you're   
prepared and your outlets       
are setup, it gets called       
once and that's it. But         
viewWillAppear obviously can    
be called every time you're     
gonna come back on screen. Now  
there's also a viewDidAppear.   
This is called to you after     
you have come on screen.        
Now, what would                 
you wanna do here?              
It's a little late to           
do things like update           
your view from your model here  
because these things are on     
screen. You don't want things   
to come on screen wrong and     
then view did appear and        
now you've clean them up. But   
this is a great place to do     
things like start an animation  
cuz you can't do that           
in viewWillAppear.              
You're not on screen,           
yeah, right. Start a timer      
that does something on screen.  
Start observing something in    
the world like a GPS locations  
or maybe the gyro position      
of your phone. Okay, those      
are all things you can do once  
you're on screen. Okay, so      
that's what viewDidAppear       
is really good for.             
One other thing that you can    
do viewDidAppear is maybe       
kick off a very expensive       
thing. Something you            
don't want to kick off          
in viewDidLoad because,         
when viewDidLoad happens,       
your view controller may not    
actually appear on screen.      
Okay, there's no guarantee      
that anyone's ever gonna        
actually put you on screen.     
So viewDidLoad is not a great,  
even veiwWillAppear,            
surprisingly, might get         
called on you, and then,        
you don't actually appear on    
screen. So viewDidAppear,       
you know you're on screen. So   
it's worth it to do something   
expensive. Now what would       
be something expensive?         
Let's say you wanna fetch       
a huge image off the network,   
okay, almost anytime you're     
gonna do something on           
the network, it's pretty        
expensive. Because on a phone,  
maybe you only have a cellular  
connection, it's a bad          
connection, you're out in       
a country road somewhere,       
it's barely getting any.        
So it could be really quite     
expensive. For the same         
reason, these expensive         
things, we almost always do     
them in the background. And     
on Wednesday, I'm gonna talk    
all about how to put things in  
the background. That's because  
it is absolutely primary goal,  
primary thing you               
must do with any                
iOS app is you never block      
the user interface experience.  
Users always have to be         
able to touch on things,        
swipe things around.            
If they swipe or touch and      
your app is frozen, they will   
not use your app, believe me.   
Okay, so that is of primary     
importance. And the way we do   
that is by putting things that  
would block, like waiting for   
something on the network, off   
the main queue, we call it,     
off the main thread into        
these background processes.     
So we'll talk all about         
that on Wednesday, but          
viewDidAppear is a great place  
to kick those things off.       
Because you don't wanna waste   
their cellular data usage       
fetching an image if you're     
not actually gonna appear.      
Now what this means though is   
since you're kicking this off   
in the background, when you've  
already appeared on screen,     
you have to design your UIs So  
that they always work even if   
the expensive thing has not     
come back yet. Do you see why   
that is? That expensive thing   
might take ten minutes or       
might never come back because   
of bad network, for example.    
So you have to put place        
holders or spinning wheels or   
loading, dot, dot, dot or       
some animation to shows the     
user, yeah, I'm fetching your   
thing. I'm working on it. But   
your UI still has to be fully   
responsive. If they're          
in a navigation control,        
they have to be able to hit     
back and give up. Okay, or      
they have to hit a tab at the   
bottom and go to a different    
tab or whatever they wanna do.  
So designing UIs,               
they're like that, take         
a little bit of adjustment.     
So far, you haven't had         
to do that probably. So         
you're mostly                   
thinking linearly.              
Okay, I am going to put         
this image on screen, so        
I will go get the image.        
Then I will put it on screen.   
Well, you can't think like      
that. You have to think,        
I wanna put this on screen, so  
I will put an image displaying  
thing on screen that is not     
loaded, with a little spinner.  
And then, later, and the user   
can do whatever they want.      
And then later when             
the picture arrives,            
now I will update my UI to      
show that image. Okay, so       
it's a little bit kind          
of thinking with that           
other dimension,                
that dimension of time.         
Okay, all right, so there's     
also view will disappear.       
You get this right              
before you go away.             
This is a great place           
to undo what you did            
when view did appear.           
So if you started a timer or    
start some animation or         
started watching GPS, or        
something like that, this is a  
good place to stop doing that.  
Because you now know that       
you're going to disappear. And  
then when you reappear,         
you'll turn it back on and      
view did appear. So they kinda  
work together, these two. And   
then there's viewDidDisappear.  
Okay, this one gets sent to     
you after you've                
completely disappeared.         
Usually, we don't               
do much here.                   
But you could imagine maybe     
cleaning up your MVC here,      
possibly saving some state or   
something. But this one is      
rarely used. All right,         
I said that in viewDidLoad,     
you cannot do geometry.         
So where do you do geometry?    
Well, you might think,          
I can do it in viewWillAppear.  
Cuz I'm just about to appear,   
and so my geometry will         
be right, no. You would think   
that, but that would be wrong.  
You obviously can't do          
it in view did appear,          
because now you're              
already on screen.              
It's too late, okay,            
to do anything about geometry.  
So the place you do geometry    
is in these two methods,        
viewWillLayoutSubviews() and    
viewDidLayoutSubviews().        
These two methods are sent to   
your controller when its view,  
the self.view, the top level    
view of the controller,         
that bar, view. When that       
view, just before and           
just after, that view is sent,  
layoutSubviews.                 
So why would that top level     
view be sent layoutSubviews?    
Same reason any other view.     
Maybe subviews came and         
went out of it.                 
That's a common one.            
Or its bounds changed. So this  
is why this is a good place to  
put geometry changes. Because   
here, this is always gonna      
happen when the bounds of       
your top level view change.     
Now usually, you don't need to  
implement these methods, why?   
Because you use an auto         
layout, right? All that Ctrl    
+ dragging, pinning things      
to the edges, keeping things    
in the center, fixing aspect    
ratios, all that business,      
you're doing that. And that is  
automatically being evoked in   
the layoutSubviews of           
your top level view. So         
you don't actually need to do   
anything in your controller.    
Okay, but if you do feel like   
you need to do something to     
your controller, this is        
definitely the place to do it.  
Now one thing about these two   
methods you have to be aware    
of, they could be               
called quite often. And         
at times you might be           
surprised like, whoa,           
nothing changed. The bounds     
didn't change, nothing          
changed with the subviews,      
why did this get called?        
Well, the system is allowed to  
layout the subviews, ensure     
the layout of the subviews of   
any view at any time. Maybe it  
wants to do an animation and    
it's laying out the beginning   
and end stage that it's going   
to do a flip between or         
something. And the bounds       
have not changed but it wants   
to make sure the views          
are laid out. Or                
it's laying them out in         
the destination, the end of     
the animation etc. So           
it doesn't really matter why,   
but the system is allowed to    
call it anytime it wants.       
And so since it can call        
layoutSubviews anytime,         
these could also                
be called anytime.              
So you wanna make sure this,    
if you do something as          
a reaction to your bounce       
changing, you wanna make sure   
these methods properly and      
efficiently work even           
if they're repeatedly called.   
They might be called twice in   
a row with the exact            
same bounds, for example.       
So you wouldn't wanna, if       
you had to do something very    
complicated and expensive,      
you certainly wouldn't do       
it twice in a row. Okay, so     
that's geometry. Now there's a  
special case of your geometry   
changing, which is              
autorotation. So                
this is you turn your device    
from landscape to portrait or   
back and forth. And when this   
happens, of course, you get     
that layoutSubviews thing,      
because your view's bounds      
change until you get            
viewWill/DidLayoutSubviews.     
But you also have this          
animation that's free           
going on. So iOS automatically  
animates the moving of all      
your subviews from              
the portrait layoutSubviews     
to the landscape                
layoutSubviews positions.       
It does that for you.           
Now that's great, that saves    
you a lot of work. But if for   
some reason you wanna           
participate in that animation.  
Now why would that be?          
I don't know if you, have you   
guys ever looked at the         
calculator app on the iPhone?   
When it's in portrait,          
the buttons are really big and  
there's not very many           
calculation buttons like        
times, plus, minus, equals.     
That's about it, right? But     
when you switch to landscape    
now the buttons get small and   
there is a whole bunch.         
Square root, algorithms,        
all kinds of things.            
Okay, well,                     
when this rotating happens,     
the calculator app wants to     
get involved, cuz it wants to   
animate those extra buttons     
appearing on screen, maybe.     
Okay, and also the layout       
changes from being kinda big    
buttons in the middle to those  
buttons being on the side and   
new buttons appearing.          
So that's where you might       
wanna participate. So           
how do you use this method?     
Well, view will transition to   
size with coordinator to do     
that. Well, the coordinator     
that's passed you is            
the animation coordinator.      
It has a method in it called    
animate alongside transition.   
And in there, you can           
provide an animation block,     
a closure and do your           
animation. And that animation   
will be performed alongside     
the animation that the system   
does when it rotates. Okay,     
and I'm not gonna talk anymore  
about it than that. It's        
just so you know it's there.    
90% of the time, you don't      
need to do that. Now,           
in your current assignment,     
assignment three, or            
assignment four, you probably,  
if you're far along on it, or   
if you're not,                  
you'll notice eventually,       
that you've got animation,      
like cards being dealt out.     
Well, what happens if you       
start dealing out a card,       
it starts flying                
across the screen,              
and then you rotate. Rotate     
your device. Well, what's       
gonna happen is this is going   
to try and do some animation.   
Now, I specifically put in the  
assignment, don't worry about   
that. You don't have to worry   
about that case this week. But  
you might ask,                  
what do I do there?             
Well, you have to understand    
that that might happen and      
deal with it. Because when      
you're throwing a card out      
across the screen there, and    
this thing starts to animate    
that same properties            
of that same view,              
it's going to put it            
possibly in the wrong place.    
Cuz it's gonna move             
it to a new place.              
It probably does animation      
begin from current state. And   
so you could end up with views  
totally in the wrong place. So  
you need to be able to          
setup your animations so        
that if they get taken over     
by someone else, all right,     
that they end up in the right   
place at the end. Okay, and so  
that can require a little       
bit of tuning, like I said,     
this animation                  
tuning business.                
That might require some of      
that. But not required for      
assignment four. It's a little  
bit of an advanced animation,   
so you have to deal with that.  
All right, this last one and    
definitely the least            
is low memory.                  
This would only be, you would   
have only any interest in this  
if your app had large memory    
items like videos. Images,      
huge sound files. Very high     
resolution big sound files for  
example. And you know it is     
possible that an iOS device     
that had many apps that were    
doing a lot of big photo        
manipulation or something.      
That it could possibly          
run low on memory.              
Most new ones have so           
much memory this doesn't        
ever happen. But                
maybe if you had an app that    
had a memory leak that was      
leaking big images in memory    
then it could happen. But       
anyway, you'll be sent          
this when that happens.         
And all this is asking your     
view controller here is         
please release anything from    
the heap that you can recreate  
fairly easily later, or         
when you need it. And so        
it's kind of a memory           
cleanup kind of thing.          
One thing I will say is         
if your app is leaking          
in the heap and you are         
getting larger and larger, and  
it sends this to you, and       
you have bad code and           
you don't know how to clean it  
up and you continue to leak,    
iOS can kill you. Okay, so, if  
you are a big memory hog and    
you keep leaking, you can be    
killed. So, just. It's kind     
of the dangers of being         
a bad programmer I guess.       
I can get killed. Almost never  
happens though. All right.      
The last thing is actually      
back to the beginning ,which    
is waking up from a story       
board. This is not strictly     
part of the view controller     
life cycle. But I put it in     
here because any object that    
comes out of a story board,     
all your UI views, and          
your view controller,           
get sent awakeFromNib.          
This is sent very early,        
right after initialization,     
before preparation,             
before your outlets are set. I  
tend to stay away from trying   
to use this unless I really     
do need something set early.    
Now we used this in the demo    
the other day cuz I wanted my   
master of my split view to be   
the delegate of the split view  
really early before the split   
view started collapsing things  
on top of other things.         
I wanted to get myself in       
there as the delegate.          
But normally you don't put      
things in awake from NIB.       
And I would say try to put it   
one of the other ones first,    
and if you really need it this  
early, then, you can go and     
do it in a way from nib.        
Of course, this is only gonna   
work for MVCs that come out     
of storyboards, but again,      
that's almost always yours.     
Your ones always come out of    
storyboards.                    
It's only the system ones,      
like the camera one, that       
you get Get from code,, so      
here is a summary of            
View Controller lifecycle,      
you're instantiated,            
usually from a storyboard,      
but sometimes you               
can ask IOS for                 
a View Controller.              
Then you get awake from Nib,    
if you're coming out            
of a storyboard.                
Then Segue preparation          
happens, in case some other     
MVC prepares you, note that     
your outlets are not set yet.   
Then your outlets get set.      
Get wired up by iOS. Then,      
viewDidLoad gets called,        
all right? Then you appear and  
disappear off screen, and       
you get those viewWill and      
did disappear, appear and       
disappear, back and forth.      
At any point along here,        
your geometry might change,     
causing vewWillLayoutSubviews   
and viewDidLayoutSubviews to    
get called. Probably with auto  
layout happening in between     
those two calls because         
LayoutSubViews will cause auto  
layout to happened and          
if at any time you start        
using a lot of memory or        
even just the whole device.     
Use a lot of memory you might   
get didReceiveMemoryWarning     
which you can hopefully         
clean up something to but       
often you can't because you     
don't have anything big.        
All right, so that is           
the View Controller Lifecycle.  
Now I have a little demo here,  
I'm not gonna spend too much    
time on this demo because we    
got scrollView to demo here,    
but, what I'm gonna do is show  
you a little piece of code,     
that you can drop               
into your app.                  
It's a sub class of             
UI view controller,             
and you can just change all     
of your view controllers to     
inherit from this instead       
of UI view controller, and      
it will essentially just put    
a print statement of all of     
the view controller             
life cycle methods.             
So you can look in              
your console and                
see what's happening in the     
view controller life cycle.     
I;m gonna do it a little bit    
today just to show you kind of  
a couple of interesting         
things about that.              
So I'm gonna do this to         
concentration, okay,            
here's our concentration,       
by the way,                     
this is where we left off last  
time. Here's awakeFromNib,      
oops, super.awakeFromNib.       
AwakeFromNib you also wanna     
call super even though, again,  
it's not strictly if you        
control the life cycle method.  
You still wanna give you super  
class the chance to do it. So   
how does this code that         
I wrote work? Okay,             
here it is right here. Where    
is it? Somewhere over here.     
I'll get these things           
out of the way.                 
There it is! Okay,              
here it is right here.          
Let's drag it in and            
take a look at it.              
I'm going to copy it in, so     
what this thing look like is    
exactly what you would think.   
It's a subclass of UI           
view controller. And            
it just has prints for all you  
did load view load appear did   
receive that morning layout     
subviews will transition even.  
It just prints them out on      
the screen when it happens.     
It also has kind of a cool      
little var, this thing,         
vclLoggingName. When it logs    
it on the console it will       
use this string to identify     
your viewController. Or         
if you don't subclass this      
it will just use the name       
of the class.                   
which is the name               
of the struct or                
class you are in. String        
describing type of self.        
That's how you do that. So,     
I'm going to make my two view   
controllers in concentration,   
right? I've got the theme       
one and the game one.           
So here's the game one.         
Instead of having it inherit    
from UI view controller, I'm    
going to have it inherit from   
VCL logging view controller.    
So now it inherits ability to   
print out all of those view     
controller life cycle methods.  
And I'm going to                
override this,                  
what did I call it,             
VCL logging name.               
And I am going to return Game,  
just because it will make our   
console output                  
a little clearer,               
which view controllers          
are which. When it's logged     
in. So this is gonna be called  
game instead of calling it      
ConcentrationViewController,    
which is kinda long and         
very similar to                 
ConcentrationThemeChooser or    
whatever it is, view            
controller. And I'm gonna do    
the same thing with my          
themed view controller here,    
I'm gonna have it inherit from  
VCLLoginViewController and      
I'm gonna change its name       
You see a log in name to be     
TeamChooser. So,                
that's only you need to do,     
in fact you don't even need to  
do this part if you just make   
your, your controller in here   
from this cell log in detail,   
it will log. So let's going to  
run, let's run this on iPad,    
see what happens. Let's go      
bring our console up here so    
we can see what's going on,     
all right?                      
So here is our concentration    
game, showing up in landscape.  
And you can see right away      
look at this init from          
coder, this init from coder is  
when something comes out of     
a storyboard. So                
both of these are initialized.  
Both of them awake from Nib,    
you see?                        
Both of them have their         
outlets and everything set.     
And then viewDidLoad            
happens on both of them.        
They both are told              
that they will appear.          
They get their sub views laid   
out a few times. Look at this,  
viewDidLayoutSubviews.          
This one gets laid out twice    
even though their bounds        
have not changed.               
Then the theme chooser          
gets a viewDidAppear and        
the game gets a viewDidAppear   
and they appear on screen Now   
what happens if I rotate this   
to portrait. Now when I go to   
portrait, I get theme chooser,  
the master will disappear,      
because it disappeared, right?  
It's no longer here,            
it no longer showing. And I     
also get this whole transition  
to with coordinator,            
because it was a rotation,      
so we have an opportunity to    
participate in the animation    
if we want. The game,           
of course, got a new size.      
See, it's 768 by 1024, so we    
got LayoutSubviews sent to it.  
And then the ThemeChooser also  
got its size laid out, for      
some reason. And                
then it disappeared. Now,       
if I drag it out,               
let's see what happens.         
Now I get the subviews layed    
out and view will appear and    
view did appear for             
the theme chooser.              
You see what's going on here?   
So as this is happening,        
it's telling us exactly what's  
going on. Now I'm gonna show    
you one other thing about this  
which is kind of interesting.   
Let's go to our                 
Concentration theme choose.     
We were already there,          
theme chooser view controller.  
I'm gonna make it, we put       
a lot of code in here so        
that it wouldn't segue if it    
could. So I'm gonna make it so  
it always segue just by         
commenting this out.            
So by doing that, I've          
made is so that every time      
we choose a game theme,         
it always segues. And           
I do that because I wanna show  
you, let's even go over and     
do this on iPhone just to       
see what's going on there.      
I'll show what happens in the   
view controller lifecycle when  
you are segueing, cuz it's      
important to understand that.   
Okay, so lets bring this up,    
and then,                       
sorry. See this happen here     
from the start? Okay, so        
this is an interesting, a very  
interesting start right here    
because we see that we          
created them, we wait for       
a viewDidLoad them, we got      
the game view well up here,     
but it never did appear,        
did it, right? Because          
we did that thing where we      
stop the collapse, remember     
the stopping the collapse and   
the swiving controller,         
it never appears. So            
it got view will up here but    
never viewDidAppear cuz it      
didn't actually appear.         
Just another reason we wanna    
wait 'till viewDidAppear,       
sometimes to kick off           
something expensive,            
all right? And when they        
both got laid out, and          
the chooser did appear.         
Now when I click on one,        
here it creates a second,       
a new, game MVC,                
you see that? It's new, this    
2 means it's a new instance.    
So this is the instance         
number of it. And notice,       
look what happened              
to the first one,               
the one that never appeared,    
it left the heap. And           
that's what happens             
when you segue.                 
You get a new one and the old   
one leaves the heap. So         
the new one gets viewDidLoad,   
viewWillAppear, viewDidAppear.  
If I go back,                   
I get some viewDidAppear and    
disappear cuz this              
one appeared and                
the other one disappeared.      
And if I segue again,           
now I get a third MVC, and the  
second one leaves the heap.     
So this is what I was saying    
about, when you do segues,      
a new one is always created,    
and                             
the old one gets thrown out of  
the navigation controller and   
leaves the heap. Everybody      
got that? Okay, that's          
all I wanted to show you. This  
is more just code that you can  
use to kind of figure out       
what's going on in your own     
app, if things are not working  
the way you expect. Okay,       
let's head back over here,      
continue our slides and         
talk about ScrollView.          
Okay, so ScrollView,            
you know what a ScrollView is,  
right? You have a little area   
on screen, and you want         
to look at something big,       
so you gotta ScrollView. You    
can scroll around with your     
finger and pinch in and         
out to zoom in on it.           
I just wanted to make sure you  
understand the ScrollView is    
a very powerful UIView.         
Okay, and it works              
really like a UIView in pretty  
much all circumstances.         
Here's my favorite              
little demo, a little,          
what do you call it recording   
this show in this class.        
Because this I believe is an    
iPhone one that you're seeing   
up here, but                    
even back from the iPhone       
one ScrollView did this.        
Now watch what happens here     
when I start this thing going.  
You can see that I've got a     
ScrollView, a horizontal one,   
that I can scroll between, and  
inside it are vertical ones.    
So it is perfectly fine to      
have ScrollViews                
inside ScrollViews.             
Here's another one, okay,       
where we're scrolling           
horizontally between these      
stock quote views or whatever.  
And then we get there, we can   
scroll up and down. Okay, so    
ScrollView knows how to         
live within itself and          
knows how to live in            
virtually every environment,    
ScrollView is really            
amazing little class. So        
let's talk about how            
ScrollView works,               
it's actually very              
simple to use as well.          
You already know how to add     
subviews to a regular UIView,   
so I have regular UIView here.  
I just create a view,           
set its frame, and add it as    
a subview, it appears, right.   
Surely the same three, you are  
intimately familiar with doing  
this. So how does it differ if  
it's a ScrollView? Okay, well   
in the ScrollView, there's      
one kind of huge difference,    
which is that we create         
what's called a contentSize.    
When we specify this            
contentSize, we're specifying   
the size of the area that       
the ScrollView is gonna scroll  
around. Okay, so this is the    
big thing that the ScrollView,  
which is little, is going       
to look around in. And          
it is only a size, it's origin  
is always 00, this little       
content area right here, so we  
always just specify the size.   
So this var, contentSize,       
that you see right there        
in ScrollView is crucial.       
It's the most important var     
maybe on any class,             
anywhere. It's the thing        
that tells the ScrollView       
how big the space is. But       
after that, adding subviews     
looks just like another view.   
Now I can create a frame, add   
a subview to the ScrollView,    
and instead of putting it       
in the black square over        
there on the left, it puts it   
in that content area instead.   
Same frame, but                 
it puts it, you know,           
still uses the frame to place   
it. But it's doing it in        
the content area. And I can     
put other things in there,      
you know, multiple subviews     
as many as I want, so           
here's two of them. And now     
the ScrollView, what it does,   
it just lets you have a little  
window that is scrolling        
around as the user pans around  
here, and of course they can    
zoom as well. Now you can       
reposition the subviews in      
that content area just          
like you can in a regular       
view subview, right?            
So I just new frame, set that   
thing to have a new frame,      
it repositions. I set the logo  
to have a different frame,      
it repositions. Now I have all  
this white space on the edges,  
I don't want it anymore, so     
I can just reset my content     
size to fit the aerial frame,   
right? Now when I scroll        
around, I'm just looking in     
this new content area. Got it?  
So that's it, so just that      
content size is the the key.    
Now, because of this magic,     
there's a couple other          
interesting things about        
Scroll View like it has         
a var called content offset.    
Content offset is just          
the x and y position            
of the upper-left corner of     
the, what's the ScrollView      
is currently showing in that    
content areas coordinate        
system. So it's just kind of    
exactly what it sounds like,    
the offset of the content that  
is being shown right there.     
And you also, might be zoomed   
though. And if you're zoomed,   
you're probably much            
more interested in,             
what is the rectangle in the    
content area coordinate system  
that I'm looking at?            
Cuz maybe I'm zoomed            
the way in on one of the        
windows there in the quad, and  
I wanna know that window. Well  
that's gonna be a very small    
rectangle, that window,         
cuz I'm zoomed way in on it.    
And the way we do that is       
using the same methods that     
you probably use or you might   
have used in your assignment.   
Which is UIViews methods        
convert from and convert to     
points and rectangles, those    
all work in UIScrollView.       
So if you say convert from      
the ScrollView to this aerial   
views, you will get the bounds  
of the ScrollView in            
that view's coordinate system.  
So that's how you can find out  
that rec in the aerial view's   
coordinate system. Just normal  
convert, nothing special        
there. Okay, so how do          
you create a ScrollView?        
Well, you can drag one out,     
of course, out of the little    
object palettes. You can        
also select a view in your      
storyboard and go to the embed  
menu as simple as you say,      
embed in navigation             
controller. And say,            
embed in ScrollView and it      
will wrap a ScrollView around.  
Unfortunately, it puts          
about a 20 pixel 20 point       
border around it which I don't  
really like but it does.        
I'll show you that in           
the demo today and              
how we got rid of that. So you  
kinda do that either way or     
you can obviously create them   
in code, right? It's just       
a UIView so it has the init     
with frame initializer to do    
that as well. So once you've    
created your ScrollView or      
put it in your storyboard and   
created an outlet to it,        
then you just say addSubview    
to add your subviews to it.     
But this will do nothing,       
nothing will appear             
screen until you set your       
contentSize. If you don't set   
that contentSize,               
then the ScrollView             
will be scrolling over a        
little zero width zero height   
rectangle in the corner. And    
your, if you put a Subview in   
there, it probably will spill   
over and show but there will    
be no scrolling. So if you use  
a ScrollView, and your image    
appears, or your views appear,  
but then you can't pan or       
pinch, it's probably because    
your contentSize is zero.       
The panning and pinching is     
only happening in the content   
area, all right? You can        
scroll programmatically,        
of course the user can          
scroll with their finger.       
But you can do it in code by    
saying scrollRecToVisible,      
you just specify a rectangle    
in the content area and         
it will slide over to           
show that rectangle.            
You can control a lot of        
other things that I don't       
have time to cover              
in scroll view.                 
For example, when a scroll      
view first appears on screen,   
it can flash its                
ScrollIndicators. Okay,         
the two things that kinda       
show you where you scrolled     
on the edges. They can flash    
that's the default in fact.     
But there's this bar flash      
scrolling indication you can    
turn it to false if you don't   
want it to do that etc. Also,   
scroll view is super            
smart about safe areas.         
Okay, remember we talked about  
safe areas like the place at    
the top of the iPhone 10? Or    
you're in a navigation control  
in the title, or                
a tab bar at the bottom, or     
it's like not like safe to      
draw there? Well scroll view,   
even if you make the scroll     
view underneath those safe      
areas, it will let you scroll   
your content into the safe      
areas. But if you scroll        
all the way down,               
or all the way over, then it    
will make sure your content is  
not obscured by the safe area.  
You'll see this in the demo.    
So scroll view, really super    
smart about safe areas.         
Because it is often the case,   
that when we have scroll view,  
we might wanna go full screen   
to get as much of our image on  
screen as we can. But, you      
know, if we got some pixels     
in the upper left corner,       
we don't want them to be,       
you know, underneath            
the little black bar at top     
of the iPhone 10. So we want    
to be able to make sure we      
scroll down to that far.        
Now what about zooming?         
So far we've only               
talked about panning,           
moving the scroll view around.  
What if we want to zoom in      
on what we're looking at and    
see it larger? Now, one thing   
when you zoom in by the way,    
be careful. That's obviously    
gonna affect your contentSize.  
If I zoom in to make what       
I'm looking at bigger,          
the content area has to         
get bigger as well. So          
sometimes that happens and      
then people are expecting       
their content area to be the    
same old size and it's not.     
Okay, now scroll view           
automatically sizes it for      
you so you don't have           
to worry about it.              
But, it's just if you ever      
look at your contentSize,       
while you're zoomed in, it's    
going to be larger, or smaller  
if you zoom out. And take       
note of the contentOffset,      
obviously. All right, so        
how do you make zooming work?   
Well, you have to do two        
things. And don't forget these  
two things. It will not work    
without these two things.       
The first one is to set the     
zoom scale min and max. Okay,   
so this is just saying how      
much do you allow to zoom in    
and how much do you allow to    
zoom out. By default, these     
are both 1.0 which means you    
can't zoom in or zoom out.      
It has to be the identity size  
right there. Now, zooming and   
scrollView works by modifying   
the transform, right?           
We know what                    
the transform is right?         
The thing you can rotate,       
scale or translate.             
Well, it is using the scale     
part of a transform to scale    
one of the subviews, we'll      
talk about which one that is,   
up and down. Okay, that is      
how a scrollView works.         
That is all it does by          
the way. Okay, when you         
zoom in and out, it's not       
doing anything else except for  
transforming the view           
that you specify. So            
this tells it how much it can   
modify that transform. So       
if it's 1.0, the transform is   
always the identity matrix and  
so It's never gonna zoom in or  
out. So you have to set these,  
at least one of these two, or   
you won't be able to zoom.      
The second thing is             
to specify that view            
that gets transformed. Because  
remember that the scroll view,  
that content area is allowed    
to have multiple subviews.      
Like we had the logo, we had    
the aerial view. Okay, so       
which, when I pinch,            
which of those gets zoomed.     
So, you have to actually        
implement a delegate method     
in scroll view.                 
Yes, that's right, if you want  
to zoom on a scroll view,       
you have to implement           
this delegate method. And       
that delegate method is called  
viewForZooming in scrollView,   
and it just returns which of    
the subviews to zoom. Now,      
to be honest, normally,         
If we had multiple subviews,    
we'd put them inside of         
a view, a top level view, and   
that's the view where           
we would zoom on.               
And of course when we           
transform that view,            
all the subviews will           
get zoomed as well.             
We wouldn't usually have        
two different views that        
are subviews in the content     
area of the scroll view and     
they're not sub views of some   
common view. But it's possible  
to do, but we don't usually     
do. Okay, you can also zoom     
programatically. In addition    
to pinching to zoom,            
you can set the zoom scale and  
also zoom to a rect.            
And here's what that looks      
like. So here I, my zoom scale  
is set to 1.2. That's 20%       
larger than the identity        
matrix. Here I've gone back     
to identity matrix. See how     
that zoomed out a little?       
Now we'll go back to 1.2.       
See, it's 20% larger, right?    
Even this would not work by     
the way if I don't set my       
minimum and maximum zoom scale  
to allow this. Now the rect is  
even cooler, so if I had this   
yellow rectangle and I said     
zoom to rect it will zoom       
the whole thing up so that      
the rect just barely fits. Or   
if I had a rect that was much   
bigger, then it would zoom it   
down to fit, so zoom to rect    
is a cool way to zoom in and    
out. All right, so ScrollView   
has a lot of delegate methods,  
like 12 or 15, somewhere in     
there. I'm not gonna talk to    
you about all of them.          
You already met one of them.    
That's view for zooming and     
ScrollView, but                 
here's another one, just give   
you an example. This one's      
called ScrollViewDidEndZooming  
with view atScale. And          
this gets sent to you           
when the user pinches and       
then lifts the fingers up. Now  
remember, as they're pinching,  
all that's being changed is     
the transform of the view for   
zooming, that's it,             
nothing else is                 
happening in there. And         
when you let go, what if your   
view, if you are capable of     
drawing your view,              
much higher resolution when     
it is zoomed in. For example,   
let's say your view draws an    
arc with core graphics. Draws   
an arc, you know an arc is      
just a bunch of little pixels,  
right, coming around here.      
If you zoom way in,             
it's gonna look like little     
blocks. Kind of jaggedy,        
right? Well, here when you get  
this DidEndZooming atScale,     
you could set your transform    
back to the identity matrix.    
And draw your,                  
draw a rect bigger. So          
that you're using that arc and  
now you get a nice smooth arc.  
You see how this would work?    
So this is a good way to draw   
with higher resolution after    
you pinch in. You're still      
going to be doing transform     
while it's pinching, which is   
good. High performance, and     
all that, but then when         
you let go, it redraws,         
kinda sharp. Okay so,           
I'm gonna do a demo here of     
ScrollView. Hopefully I         
should have time to do,         
show you all these stuff that   
I just talked about. And        
then on Wednesday, we will      
learn about multithreading,     
okay, putting things            
in the background. And          
we'll update the app            
that I'm writing today to       
do its image loading and        
all that stuff in               
the background.                 
Now I'm going to do that        
kind of background stuff by     
doing it over the network.      
Which I was going to show you   
a little bit of today but       
my network doesn't work         
on my new laptop. So,           
I'll just be using local image  
file today, which is fine, but  
on Wednesday, I'll get my       
laptop working. And we'll be    
able to do networking, because  
when we load images over        
network, they're slow. And you  
can see it's called Cassini,    
that's because the images       
we're gonna load up are from    
the Cassini probe that          
was sent out to Saturn and      
they're big. Big and very,      
very high resolution. And       
then even on Stanford's fast    
network, they take              
a few seconds to load.          
And that, we can't have our UI  
blocked by that. Wednesday,     
I'll also talk about            
text field, and                 
maybe I'll fit in one           
other little UI element,        
time permitting. We do have     
a Friday section this week,     
it's on this app called         
Instruments, which is used      
to analyze the performance of   
your apps. So don't miss that.  
And then next week we'll dive   
into probably table view and    
collection view, drag and       
drop kind of more powerful,     
more sophisticated things that  
I haven't been able             
to teach you yet,               
because you really needed to    
understand delegation and       
things like that before we      
dive into those. All right,     
let's do Cassini here.          
Oops, I quit,                   
I didn't wanna quit it.         
Okay, I'll start it back. So    
we're gonna create a new        
project here for Cassini and    
it's gonna be single view app.  
I'm gonna call it Cassini.      
Not gonna do anything special   
here. We're not gonna do,       
you've all learned about        
source code control now, but    
we're not gonna do that here.   
Okay, here's our Cassini.       
I'm gonna do what I usually do  
here, but take the assets and   
the app delegate and            
the launch screen, and          
kinda move them out of the way  
into supporting files so        
we don't get confused by        
seeing them repeatedly. And     
I'm also gonna do something     
interesting here as well.       
We get, when we create          
that single view app,           
we get this little view         
controller for free and         
this is its controlling view.   
And a lot of times, or          
only thing we've done so        
far, is rename this to be       
a concentration                 
view controller, or             
you probably had set view       
controller in yours as well.    
But there's another way         
to do this as well,             
which is I can just take        
this and delete it.             
So now I have a completely      
empty storyboard.               
There's nothing in here.        
I can delete this, too.         
Let's just delete that,         
get that out of here,           
move it to the trash. So now    
my app is completely empty.     
The only thing I have is this   
supporting files here, but      
there's basically nothing. So,  
there's nothing going on here.  
So, that's perfectly allowed.   
Now if we do that,              
how do I build it back up?      
Well, I'm going to build an     
app here, Cassini. Initially,   
it's not going to have nothing  
to do anything with Cassini,    
it's just going to have         
an image in a scroll view.      
That's going to allow you       
to choose an image and          
put it in a scroll view.        
So I wanna create my own view   
controller that shows an image  
and I'm going to create a new   
view controller to do that      
called image view controller.   
So I'm going to go here.        
Yes, it's a view controller.    
I'm going to call it            
image view controller,          
cuz that's what it does,        
it shows an image.              
I'm gonna create it,            
I'm gonna make sure I put it    
in the right place here. And    
here's our image                
view controller.                
I'm going to delete all of      
the code out of it, just so     
it's clear what code I'm        
adding. I'm gonna go back to    
my storyboard, which is         
completely empty, and drag out  
a new view controller, and go   
to the identity inspector over  
here, and change it to be       
an image view controller.       
Now, I haven't done one thing   
that's very important that is   
causing this warning.           
You see this warning up here?   
It says image view              
controller is unreachable.      
That's because there's no       
arrow, you see? Remember        
the arrow that I moved over in  
split view controller demo?     
There's no arrow.               
So how do I put the arrow?      
There's no arrow. How do I get  
it? Well, you can do this by    
inspecting a view controller    
in the normal inspector, and    
look at this                    
button right here,              
is initial view controller,     
boom. Okay, now it'll           
come in and this will be our    
view controller. Okay, so       
let's start here by just not    
having a scroll view and        
just having an image view.      
I'm just gonna take             
an image view out of here.      
Here's one, we put in image.    
Here's an image view.           
So image view, like I said, is  
kind like UI label, except for  
it's for images.                
And I'm gonna put it in here,   
and go edge to edge, right      
there. Let's actually see what  
this looks like on an iPhone    
10, because of course we have   
safe area issues there. Zoom    
in a little. So I'm gonna move  
the size of this actually so    
that it covers the safe areas.  
See how that's in the safe      
areas right there?              
Now, what I wanna do,           
and let's go ahead and          
while we're here let's go       
get our view controller and     
create an outlet to             
this image view,                
cuz we're going to              
want to talk to this.           
So I'm just going to            
control drag into my class.     
I'm going to call this image    
view. So I'm not lit there.     
Now, I want this big image      
view right here to go all       
the way to the edges. I don't   
really want it to respect       
the safe area for now. We're    
going to have scroll view and   
it will help us                 
with that later.                
But I want it all the way out.  
It looks cool.                  
I don't mind if a little        
bit is covered by this and      
a little bit is covered         
by that. It's fine by me,       
I think it's my image will      
look cooler to do that.         
Now how do I control drag       
to the edges here? Okay,        
I can't get to any              
of the edges, right?            
Because it's right on           
the edges, so I can't pick it.  
Well, we're gonna use, again,   
I'm trying to show you auto     
layout a little bit at a time,  
one drip at a time.             
So we're gonna start using      
these little things in          
the corner here. You see in     
the lower right-hand corner?    
We already know about           
this one right here,            
embed in stack view. We're      
not going to use that. And      
we actually have                
seen this one,                  
I think, which just let's you   
clear all your constraints, or  
reset to the blue line          
suggested constraints, etc.     
But we're actually going        
to use a different one,         
which this one right here. It   
just says add new constraints.  
Look what happens if I select   
the image view and click this.  
It offers to let me set         
some of its constraints.        
Now, some of these like width   
and height and aspect ratio,    
we know we can get by control   
dragging to itself. But         
look at these ones up here.     
This is essentially pin,        
pin to the edges. So this is,   
these little I beams,           
if you turn them on,            
will add constraints.           
You see, add four constraints.  
Add three constraints. It'll    
add constraints that pin them   
to what's called the nearest    
neighbor. You see the nearest   
neighbor? So it's gonna look    
at that edge, and say what's    
the nearest neighbor there?     
Maybe it's the other edge of    
a nearby view, maybe it's       
the edge of your superview,     
maybe it's the safe area. So    
let's see what happens when we  
add these four constraints to   
our image view. We'll find      
out what the four nearest       
neighbors were to its four      
edges. So here we go. Add four  
constraints. Now I'm going      
to bring up the inspector,      
go to the size inspector        
to look at my constraints.      
Remember, that's how you        
look at the constraints on      
a view when you                 
select a view and               
go to the size inspector        
up here. You get to see it.     
And look, the bottom and top,   
it's thought the nearest        
neighbor was super view,        
self.view. My view              
controller's top level view,    
right? But look, on the sides   
it thought it was the safe      
area. That's because the safe   
area here, on this iPhone 10,   
goes across here. It doesn't    
include that thing, so          
it goes across here down this   
edge. And then across here,     
not including that little       
thing right there and           
back up this edge. That's the   
safe area. Well, obviously,     
the two sides are just as       
close to the safe area          
as they are to the edges        
of the superview.               
And always, interface builder   
will prefer the safe area       
over the superview's edges,     
if they're the same. But        
up here, this is nowhere near   
the safe area this is right on  
the superview's edge, but it's  
nowhere near the safe area. So  
that's why it shows superview.  
See why it did that? Okay,      
now, I don't want the safe      
area though here.               
I always want this at the       
edges. So how do I fix this to  
not be doing safe area?         
I want that to say superview.   
So how do I do that? Well, I    
need to edit this constraint.   
But no, how do I pick that      
constraint? I can't even,       
I can't see it, where is it?    
Well, I'm gonna introduce to    
you the way we almost always    
actually access constraints,    
which is with the document      
outline. Okay, remember this    
little button over here, and I  
said in previous lectures and   
demos, hey, don't worry         
about this for now?             
Well, we're gonna start         
worrying about this cuz this    
is showing the everything       
that's over here in my view.    
In outline form,                
including the constraints.      
See those constraints right     
there, they look familiar? And  
these are the constraints       
between this                    
view and its superview. So      
here's the two that             
are hooked to the Safe Area.    
Here's the two that are hooked  
directly to the superview.      
So let's click on one, and      
go to the normal inspector or   
the size inspector, either one  
for constraints, and we can     
edit this stuff. And we've      
already seen in previous demos  
how we edit the constant here,  
or the multiplier or whatever.  
Now we're gonna see how we      
edit the actual connectivity.   
So this constraint is from the  
images, image view's leading    
edge setting that equal to      
the Safe Area's leading edge.   
Well, I just want this          
to be the same, but             
I don't want to be              
the Safe Area's leading edge,   
I want it to be the             
Superviews. So you can change   
from Safe Area to Superview     
just by editing the constraint  
and changing the item that      
is sent, connected to.          
Same thing here, I'm gonna      
change the Safe Area Trailing   
to be the Superview Trailing.   
Now, if I select this and       
look in the size inspector,     
you see that all of my          
constraints are to the          
Superview. So now my view's     
gonna fill my entire space,     
forgetting about any space.     
I don't care if I have title    
bar for navigation control, or  
tab bars. It's all gonna be     
filling the entire screen       
behind those things.            
Or if things, this is like      
a physical thing, this is not   
gonna be able to draw there.    
But that's kinda what I want    
in this case. Well we're gonna  
see more from this document     
outlined in a moment here. But  
this is a great way to access   
everything in your view.        
It's very easy to get at        
stuff. All right, so let's go   
back to our code over here.     
And we want to write some       
code that makes this view       
controller do what it does.     
Well, this is                   
a controller of an MVC.         
What is the model of this MVC?  
Well I'm gonna make the model   
of this be an imageURL.         
It's gonna be of type,          
this class in iOS, called URL.  
A URL just represents a URL.    
It could be a local file URL,   
or it could be an Internet      
URL. And this is my model.      
And so it's my job              
to take that model,             
turn it into an image and       
present it in my view,          
which my view right now         
is just this ImageView          
right here. So I'm going to     
do that right away by saying,   
that if someone sets my model   
right here, I'm gonna set my    
ImageView's Image to nil.       
Okay, because I'm changing      
it to a new thing. And then     
I'm gonna go fetch that image.  
Now the reason I'm making this  
another little function here,   
some private func fetch image,  
is because this thing           
might be over the network.      
And eventually on Wednesday,    
we're gonna make fetch image    
work in a background task,      
obviously, because you could    
get blocked by the network. So  
I'm kinda thinking              
ahead a little bit and          
putting this in a separate      
function as opposed to just     
dropping it right in here. So   
someone set a new image URL.    
We clear out whatever           
image we have, if any.          
Image is just a bar in our      
imageView that says the image.  
And then we're gonna go         
fetch a new image over here.    
Now one thing, since we just    
learned about View controller   
life cycle. I told you that     
you don't really wanna do       
something like this fetch       
image unless you have to. So    
I'm gonna do something here,    
which is I'm only gonna         
fetch this image if someone     
sets my model and I'm on        
screen. If someone sets my      
model and I'm off screen,       
I am not gonna go fetch         
that image yet. I'm gonna wait  
until I know I'm on screen.     
Now how can you tell if you're  
on screen? How do you know if   
your MVC's on screen? Well      
here is a cool way to do it.    
I'm just gonna check to see     
if my view has a window         
that's not nil. So we didn't    
really talk about window and    
i kinda told you to ignore it   
at the beginning of class, and  
you mostly can,                 
except for that a view,         
if it's onscreen,               
we'll have a window bar, which  
is the window that it's in,     
which is usually just this one  
window. So this is a kind of    
a cool, simple little way to    
check am I onscreen, and if     
I am onscreen, then I'm gonna   
go ahead and fetch this image.  
But what if I'm not onscreen?   
Eventually I need to fetch      
the image. So when is a good    
time to do that? Anyone have    
an idea on that? Yeah,          
view did appear. Exactly,       
let's do it, viewDidAppear.     
Okay, super.viewDidAppear.      
At this point, I know that      
I'm on screen, so now           
inside of my viewDidAppear,     
I can just check to see if my   
image views image.              
Oops, image is still nil,       
then let's fetch it.            
See how I use the view          
controller lifecycle there to   
keep myself from doing any      
unnecessary work? Now, of       
course, if my fetchImage truly  
does work in the background,    
I have more work to do here,    
which is I've gotta give        
some indication to the user     
I'm working on it. Okay, cuz    
otherwise they're gonna say,    
where's my image.               
Went to this view control,      
I don't see any image up here.  
So I have to give some          
feedback. But since right       
now it's gonna block            
the user interface, until it    
gets the image cuz we're not    
doing the multithreading,       
we'll just leave it like it     
is. But we're gonna see it,     
when we do it we're gonna put   
something in there to do that.  
All right, so fetchImage,       
how do we implement fetchImage  
right here? Well,               
this is actually pretty easy.   
We're just gonna see if         
our URL, our model here,        
is non nil, cuz if it's nil,    
we don't have to do anything.   
Because whenever someone sets   
a new image, we set it to nil   
and whether we fetch it here    
or here, if there's no,         
if that image URL is nil,       
we just, we leave it blank.     
Nothing in our image view. But  
if I have it, then I need to    
go out in the Internet and      
get the data at that URL and    
turn it into an image. So how   
do we go out on the Internet,   
and get data from URL? We do    
that with the data objects.     
Remember the data               
object I talked about,          
it's just as bag                
of bits thing.                  
Well, it has an initial,        
an initializer, which is        
give me a URL, and I'll go get  
the bag of bits from that URL.  
So we can set a little          
local variable,                 
which I'm gonna call my URL     
contents, equal to data.        
Now, let's look at the various  
initializers for a data, and    
it has a lot of them            
because it's a bag of bits.     
So we can convert to a lot      
of different data types,        
right, bags of bytes and        
all kinds of things here. But   
one of them has, is this one    
right here, content of URL.     
What's that word after          
contentsOf : URL? Throws.       
So why can this throw?          
Well, it's going                
out on the network maybe,       
lots of reasons it can throw.   
Network connection is bad, the  
server rejected the request.    
There's probably 20 different   
things it could throw here.     
Now it turns out we don't care  
about any of them. If we did,   
let's go ahead and put          
contentsOf: URL in here. And    
the URL is url.                 
If we did, since this throws,   
what do we have to say in       
front of it? Try, yeah,         
we have to try it               
because it might fail.          
And we could wrap a little      
do catch let error here and     
do this thing, catch the error  
and then process the error.     
It was a server time out,       
network unavailable all these   
things. But we don't really     
care about any of that stuff.   
We're just a lowly image view   
controller, we just wanna put   
our image up or not. So we      
don't care about the error, so  
we're not gonna do it with      
a do try, do catch thing here.  
Instead, we're gonna use        
this ver, okay, right now,      
this urlContents right          
here would be a type data.      
But I'm gonna change            
this to try?,                   
which says try this thing and   
if it fails, just return nil.   
So that makes this now we have  
type Data?, right? Optional     
data, because the try might     
fail. So I've got this content  
right here. Now I'm just gonna  
say if I can let my image data  
that I got from that URL        
equal the urlContents.          
Then I can create my image      
from that data. So that image   
data is probably JPEG data or   
something like that. So         
I'm gonna say here that my      
image views image equals        
UIImage. Now UIImage, we've     
seen UIImage named, right?      
That's the one we've used.      
There's another UIImage one     
called UIImage with data. And   
you give it Image data          
which is data object.           
And it will look in there,      
that bag of bits, and           
see if it recognizes anything   
that looks like an image,       
like a JPEG file or             
something like that. So         
that's kinda cool,              
all right? So that's it.        
That's all we need to do to     
show an image here. I'm gonna   
do one other thing just for     
demo purposes here, which is,   
in my viewed load, I'll just    
do super here. I'm gonna say,   
if by the time I load           
my image URL is nil,            
then let's load up kind         
of a sample image, and          
I have a sample image           
here in a file, a URL for       
sample image. Let's go here,    
I'm gonna drag this in,         
demo URLs right here. Again,    
let me show you this demo URL,  
what this looks like.           
So the demo URL's that I have,  
I have this Stanford one right  
here. Which is a big picture    
of the oval, and then I have    
these NASA ones right here for  
Wednesday's demo. Now again,    
I don't have any network,       
so I can't actually do this     
one over the network, so        
I'm gonna do a local one.       
Using this bundle main.url.     
So this gives me a local one.   
So I have a local version of    
the oval here. I'll just        
drag it in, copy that in.       
I can show you what this        
looks like over here, right.    
So it's just a big thing        
of the oval there,              
very high resolution.           
Notice I click this button      
over here target membership     
to make sure that this oval     
when I install my app on        
the device gets included.       
Because I'm looking             
at locally, but                 
I'm just gonna get a URL to     
it. It just happens to be       
a URL to a local file as        
opposed to URL for network,     
which doesn't matter cuz we're  
not doing multi-threading yet,  
so it's perfectly fine.         
Okay, so                        
that here I'm just gonna        
set my model equal to my        
DemoURLs.Stanford. So that's    
just that URL for the local     
guy. All right, so let's run    
to see what this does for       
us. Actually I'm gonna,         
we're gonna re-run this and     
do it with iPhone 10.           
But here it is. Now it          
showed the image but that       
really long image got smashed.  
And if I go this way, it's      
doing a good job of sticking    
to the edges. But of course,    
it's kind of smashed as this    
is not Stanford in              
all its glory here.             
So we need to fix that, and     
how do we fix that? Of course,  
with ScrollView, right? If we   
put a ScrollView in there,      
it can be its normal size and   
we can scroll around on it.     
Zoom in and out and             
look at it. So that's perfect   
opportunity here, to fix our    
little app with ScrollView.     
Now, I'm gonna show you         
both ways to do ScrollView.     
Scroll view done from a         
storyboard and ScrollView done  
in code. So that you both see   
how to do it in storyboard,     
because it can be a little      
bit tricky there, and so        
you can see what's going on     
with the contents size and      
all that stuff in the code.     
All right, so let's go to our   
storyboard right here.          
And here is our ImageView.      
Now I am going to add this      
ScrollView. Again, I could      
go over here, and if you        
go down here and search for     
ScrollView you'll see you can   
drag out a scroll view and      
then just drag                  
sub-views into it.              
By the way, when you are        
interacting with the sub-views  
of ScrollView and Interface     
Builder, it's talking about     
the content area. So the        
Interface Builder doesn't even  
know anything about ScrollView  
sub-views except for            
that they're in                 
the content area. So            
for example we're gonna         
create some constraints here    
between the image view and      
the ScrollView.                 
That's gonna be constraints     
between the image view and      
the content area, that big      
white area of the ScrollView.   
So that's important to          
understand when you're working  
in Interface Builder. But I'm   
not gonna do it by dragging     
it out down here. Instead, I'm  
gonna use that embed thing,     
right? So I have my             
Image View selected here.       
I'm gonna go Embed In,          
Scroll View, and it does it.    
Now like I said, it puts this   
little 20-pixel border around   
it, which I don't really want,  
so I'm going to resize          
this to be perfectly hooked     
up to my edges right here.      
Notice that now the ImageView   
still got the 20 pixels, so     
I'm gonna move it. So I've      
kind of put these two things    
where I want. Now, it really    
doesn't matter actually where   
I put the image view in terms   
of it's size and angle.         
What's important here is that   
that ImageView be tied to       
the edges of the content area.  
And why is that important?      
Not, so that the ImageView is   
the right size, but so that     
the content area is the right   
size. If I tie the ImageView's  
edges to the content area, and  
the ImageView changes size,     
the content area will change    
size. And I have to have that   
content area always matching    
the size of the ImageView if I  
wanna be able to scroll over    
the entire image, right?        
So, when you do this in         
Interface Builder as opposed    
to in code, you do this         
with constraint by hooking      
the ImageView to be tied to     
the ScrollView. Which in fact,  
it already is, because when     
I did embed in ScrollView,      
the ImageView was already tied  
to its super view, so it kept   
it it's just that it got these  
little extra 20 points in       
there which I don't like.       
Now the thing here is this      
ScrollView itself is not tied   
to its edges. I resized it but  
it's there's no constraint. So  
I'm gonna tie it to the edges   
in the same way that            
I did the other one.            
How do I choose the ScrollView  
though? When I click here,      
dang it, I keep getting the     
ImageView. Can anyone think of  
a way we could choose this?     
>> [INAUDIBLE]                  
>> The document outline,        
absolutely. Let's go over here  
to Document Outline, right?     
And here's the ScrollView       
right here,                     
I just selected it. So it's     
really easy in the document     
outline to pick what            
you actually want. So           
I can pick my ScrollView here,  
and once I have my ScrollView   
here, I can pick,               
go down here to                 
this thing we had               
with the edges and              
just hook it to edges up to     
its nearest neighbors. Same     
exact thing we did with the     
ImageView, it does it here.     
It had the same problem where   
it put the trailing and         
leading to the safe area.       
Now in the document outline     
when you're looking for         
constraints, be careful         
about the indentation.          
These constraints go            
to the ImageView.               
These constraints go            
to the ScrollView.              
You see, how the indentation    
matters there? So               
here I'm gonna change the safe  
area of this to be SuperView    
also. SuperView.                
Okay, we got that.              
And then the last thing I       
wanna do here is fix all this   
all these 20 points thing that  
it gave me when I embedded,     
20 points,                      
cuz I don't want that.          
I can edit that right in here,  
right in my size inspector      
here. So I'm just gonna say,    
edit, make that zero.           
Edit this, make that zero.      
Edit this, make that zero.      
Edit this and make that zero.   
So now I've got good-looking    
constraints right here.         
See, the ScrollView is          
hooked up to its SuperView.     
You can see that the ImageView  
is hooked up to its SuperView,  
which in Interface Builder      
means the content area          
of the ScrollView.              
I can't emphasize that enough.  
When you have this view,        
let's say sub-view, see how     
the ImageView is a sub-view     
of the ScrollView is indented   
right there? That means that    
the ImageView, when the         
ScrollView is your SuperView,   
you're talking about the        
content area. Now, why is this  
still red? Okay, it seems to    
me like I've specified all my   
constraints between these       
views. Why is this red?         
Well let's go look up here and  
see what it says.               
It says scrollable content      
size is ambiguous for           
ScrollView. So it's talking     
about that content area,        
it's saying how big is that     
thing? Well the answer is,      
it's as big as the image view,  
because the edges of those two  
things are tied together. But   
Interface Builder does not      
know how big the image view     
is. Why? Because it has         
no image. Okay, this            
image view is empty, it has no  
image so Interface Builder is   
like I don't know how           
big that thing is, so           
I don't know how big the        
scrollable content area is for  
the scroll view. Now,           
this is an unsolvable problem,  
because we don't set the image  
until our code runs.            
So how do we deal with          
this in Interface Builder?      
Well, Interface Builder has     
a really cool little feature,   
if you inspect the image        
view right here and             
go down to the bottom, you see  
this thing intrinsic size?      
The intrinsic size of           
something is the size that it   
wants to be to fit its          
content. So for a label,        
an intrinsic size of a label    
is however small it can be and  
still fit all of                
the text in there.              
For an image view of            
the intrinsic size,             
how it can be with              
the natural aspect ratio and    
size of the image, so that's    
its intrinsic size. Well        
its intrinsic size, it's right  
now set to be the default, but  
you can actually set it         
to be a placeholder.            
This placeholder, which I'm     
gonna set, is just any random   
size you want, and it'll only   
use this in Interface Builder.  
It's just holding this place    
in Interface Builder, right?    
In code we're going to set it.  
So look, that got rid of        
my red complaint. Because now   
the imageView has a size, and   
so the content area's           
gonna be 8375 by 812, but       
only in Interface Builder.      
Once it runs,                   
it's going to be whatever       
the size the imageView is,      
which is gonna depend           
on what image we set.           
Everybody got all that?         
So this is kind of the,         
you can see what I              
was saying about it.            
It's a little bit tricky        
with the scroll view.           
It takes a little bit getting   
used to understand that when    
you are setting constraints     
between the image view and      
the scroll view,                
you're talking about            
the content area.               
And that's controlling          
the size of the content area.   
So let's run,                   
that's all we have to do,       
we don't have to do anything    
in the code here cuz we set     
all these constraints.          
So let's go ahead and           
run this thing,                 
I keep forgetting               
to do this on iPhone 10 which   
is a little more interesting.   
In fact, let's do it on iPhone  
10 and stop here. iPhone 10,    
iPhone 10's a little more       
interesting, because it has     
that safe area built into it    
whereas the other one doesn't.  
Okay, so look.                  
Here's our image.               
You see how this blue           
sky of our image is             
up here where the time is and   
all that? And if I scroll,      
woo hoo, it's showing, okay,    
our tree up here. That's cool.  
You can scroll all around.      
Notice that if I scroll down    
it'll reveal the safe area,     
right? It'll make this be       
lower than the safe area.       
So that's the scrollView being  
smart about the safe area.      
Even though we tied it to its   
super views edges it still      
knows there's a safe area.      
And it goes all the way down,   
and the same will be true down  
at the other side. So here      
we're scrolling through. We     
can go landscape for example,   
and just scroll around.         
Find the stuff we want,         
there's the dish right there.   
And so that's it.               
That's all that's required for  
scrollView. Works super easy.   
Now what about doing this in    
code? So in code, in some ways  
it's almost a little easier     
than doing it. Because          
you don't have the 20 pixel     
thing and all that stuff.       
So I'm gonna do the exact       
same thing here in, in code.    
So I'm gonna take my            
imageView and delete it.        
So I just deleted               
my imageView.                   
I'm gonna leave my scrollView   
in there, though. Because I     
need to talk to the scroll      
view to set its content size,   
so I'm gonna leave that in      
there. Let's go over here and   
get our code up                 
at the same time.               
I'm going to create an outlet   
to this scrollView right here,  
so that I can again talk to     
it. I don't need my image view  
outlet anymore, because I'm     
going to create it in code.     
In fact, let's just             
create it right here.           
I'm going to save my            
ImageView = a new ImageView,    
I'm just gonna create           
the ImageView.                  
It's sub zero size, but that's  
okay. Whenever I set an image,  
I'll resize it. And then        
let's Control+Drag to hook      
up to our scrollView. Call      
this scrollView. Now we have    
a connection to both. Here      
we just want the imageView      
to be in the content area of    
the scrollView. So as soon as   
the scrollView gets hooked up   
by Interface Builder I'm going  
to ask the scrollView to        
add the scrollView to add       
as a subview by imageView.      
So it added it. That's nice.    
Again the content area is size  
zero and so is my imageView at  
this point, so it's going       
to be completely blank.         
Any time I set image            
like I do it here and           
actually I do it                
up here as well.                
I need to tell the imageView    
to size itself to fit.          
So size to fit means make       
yourself your intrinsic size,   
right? The size that will       
fit this image the best and     
now the UIimage use             
frame has been set, but         
if I don't do this,             
scrollView.contentSize =.       
Equals the                      
imageView.frame.size.           
If I don't do that then it's    
not going to scroll at all.     
You see why? Okay, otherwise    
the content are would be 00     
size. I got this big image and  
I'm trying to scroll and        
this 00 size rect               
does nothing.                   
Okay, now I actually have to    
do this same thing up here      
when I set the image            
up here back to nil.            
I want to resize my content     
size back down to zero.         
Now normally I would say,       
copy and                        
pasting code like that          
that's bad. Don't do it.        
But this is a demo.             
I don't have time to fix it.    
But actually I'm going to       
take the time to fix it,        
because I'm going to fix it     
in a way which I think will     
be kind of interesting.         
Which is I'm going to make      
a var, actually a private var,  
called image which is going     
to be a type UIImage, and       
every, it's going               
to be computed,                 
every time I want to set        
my image or get my image,       
I'm just going to get it from   
imageView. So I'm gonna say     
get, return the imageView's     
image, and set, it's            
going to set the imageView's    
image to the new value. So      
this is just a normal computed  
property right here. But        
the cool thing is now,          
I can put this in here. So      
that every time                 
I set my image,                 
I resize my imageView and       
my scrollView. And so           
up here I can just say,         
image = nil, and down here,     
I can just say, image = the     
new image, and I don't have to  
have this code copied in        
two different places.           
So here's an example where      
you can use a computed Var,     
because conceptually            
this is my image,               
it happens to be stored         
in the imageView,               
I don't need to                 
duplicate it from there,        
I'm just gonna return it,       
and set it in there. But        
I can also do my nice little    
setting on the side there.      
Okay, that's it.                
So it's easy in code as well.   
Okay this is a big file so      
even locally it takes awhile    
to load. But here it is. So     
we got the exact same           
thing we had before,            
we can still find the addition  
here, we can rotate. But        
it would be really cool if we   
could see the whole picture.    
It's a little annoying,         
we can see just                 
two little parts of it.         
It's kind of cool, actually,    
to be able to look              
at parts of it. But             
we'd like to be able to zoom    
out and see the whole thing.    
So now we need zooming.         
So what are the two things      
we need to do zooming? Set the  
minimum, maximum's zoom scale.  
Provided delegate says          
which view to zoom.             
Now it's obvious which view we  
want the transform to work on   
here, it's our ImageView. So    
let's go do that, and I'm just  
going to do that, I'll set      
those things right here. Okay,  
here is where my scrollView is  
set by iOS so let's just have   
the scrollView's minimum        
zoom scale, we'll go 1/25,      
okay, 1/25. So                  
we'll allow it to zoom out, so  
that the image is               
1/25 its normal size.           
And then maybe for the maximum  
zoom scale, I'll say 1.0.       
Why would I say 1.0?            
Well, maybe because I don't     
want you to zoom in so that     
the bits look kinda, you know,  
pixellated, so I'm not gonna    
let you zoom in anymore than    
1.0. So you can zoom into 1.0,  
but no closer, and we'll let    
you zoom all out to 1/25,       
so that's part one, part two    
is scrollView.delegate. Equals  
what? Self, yeah. I'm gonna     
be the scrollView's delegate.   
Now, this is going to           
generate an error right here,   
because self is not a UI        
scrollView delegate.            
That's easily fixed,            
we go up here and               
say yes, I am a UI              
scroll view delegate,           
thank you very much. And since  
all those methods are optional  
Objective-C methods, all        
my errors go away. However,     
scrolling not gonna work        
unless I implement that view    
for scrolling method. So let's  
do that. It's called view for   
scrolling there it is,          
viewForZoomingIn I mean, and    
we just need to return our      
image view, because that is     
the view, the sub view in the   
content area, that we want to   
be transformed when we pitch    
all right? So let's try it.     
Okay, here we go.               
We can still pan around.        
But now, we can zoom. Remember  
remember how to pinch?          
Hold down the option key        
right here. Got low battery.    
Hold down the option right      
here, and pinch. And            
we can zoom out here.           
Okay, all the way out to here.  
Maybe look better if we do      
this, there we go. Like that,   
okay, zoom in, real easy.       
So hopefully, that gives you    
the full tour of scrollView     
in terms of adding subviews,    
managing its content size,      
doing zooming, all this stuff,  
very straightforward,           
easy to use. Next time          
what we're gonna do is the      
images that we're gonna get     
are gonna be these huge         
images over the network.        
Hopefully, I can get my Mac     
to work on the network, and     
that's going to be slow. And    
we are not gonna want our UI    
to be blocked waiting for       
that to happen.                 
We want it still to be          
highly interactive. And         
so to do that, we need to do    
multithreading. So that'll be   
our primary topic at the start  
of Wednesday's lecture.         
And I will see you all then.    
>> For                          
more, please visit              
us at stanford.edu.             
