Practice English Speaking&Listening with: Stanford - Developing iOS 8 Apps with Swift - 9. Scroll View and Multithreading

Normal
(0)
Difficulty: 0

Stanford University.

Welcome to Stanford CS193P, Winter of 2015.

This is Lecture 9, I believe.

And today, we are going to turn on our clicker here and

then we are going to.

I'm going to show you a brief, very brief demo,

in addition to the Autolayout Demo that we did last time.

I challenged you in that one to add buttons to

your calculator, then I didn't show you how to do that.

So I'm gonna show you how to do that.

How, in a different size class,

you could add some views that aren't in another size class.

Then we're gonna talk about two big topics today.

One of them is ScrollView, okay.

And the second one is Multithreading.

And, kind of as a preamble to do multithreading,

I'm going to talk a little bit about closures.

Now, you should all have read your reading assignment, so

you all know about closures.

But, I just wanna emphasize something about them.

That will start to be more important as we

start using closures more for things like multithreading.

So, let's make a quick little detour here.

I'm just gonna open up Autolayout here.

So here's autolayout.

And what I wanna be able to do is add another

view only in one of my size classes.

So here's my Any, Any size class.

And let's say I go back to my Any Width,

Regular Height that we were working on.

And let's say, when we're in this particular arrangement,

we wanna have,

you know, an extra button up here or something.

And all we need to do that is to drag out whatever

the extra UI you want is, like this Button.

And if you inspect the button with the Inspector,

Attributes Inspector.

Down at the very bottom, you'll see, right here,

that there is some switches that control where this

button appears.

Now, since I dragged this button into this size class,

it assumes I only wanted it in this size class.

You see where it says Installed, right here?

I could put it in all size classes by clicking this.

That means it'll be in all size classes.

Or I could take it out of this size class only,

by unclicking this, okay?

And you can add more things.

I could say in Compact Width,

Compact Height, I want this button to be there or not.

Okay?

And that's true for constraints, as well.

If you have a certain constraint.

Like we've got constraints down here,

like this one right here, that are only in this size class.

Because these things are only down here in this size class.

So you can see that's switched.

So, whether something is even there and

also the constraints, you can control right here.

And someone asked me, oh, I did all this work in here, for

all this size class, but I changed my mind.

I don't want it to be different in that size class.

How would I go back and undo all this?

Well, the way you would do

that is bring up your document outline.

And you can select all your constraints.

You can actually shift > click to select.

I could select all my constraints.

And you can see over here, in the Inspector,

it's going to show me that, you know, some of.

It's got a dash here instead of a check.

So, kind of some of them are there.

And if just go here and say, boom, and remove that.

Now, all my constraints are only in my base class.

There's no constraints that are just in this one.

And, in fact, if you look down here.

Look, all these constraints are all gone.

So, in fact, if I go here and Update Frames.

Boom, it goes back to the way it was.

You see?

Everyone understand that?

So, Constraints and the Views can be turned on and off

by just inspecting them in the Attributes Inspector, okay?

That's all I wanted to show you there.

All right, back to the slides.

All right, so let's.

Talk about ScrollView.

All right so, you can see this iPhone right here.

This is like,

I don't know, iPhone 1 or something-looking thing.

But, I keep it around because it's a really cool example of

the power of ScrollView.

And what you can do with ScrollView.

Especially with ScrollViews inside ScrollViews.

So, watch this little movie here go as we scroll around,

up and down.

Okay, so that's just normal side-to-side scrolling.

But there's up and down within the side to side.

When I get to one, I can go up and down.

And then this app, at the bottom, I can scroll side to

side at the bottom and also up and down.

So, you see how the scrolling can kind of

go both directions.

Depending on what you've scrolled into view.

So, ScrollView is very smart about knowing which

way you're scrolling.

And having, you know, opposing ScrollViews and

stuff work properly, okay.

Now, we're not gonna be able to have time, in the brief

time we have here, to discuss how to make all that work.

But I just want you to know the power is there.

So, someday, if you wanna build an app that has some UI

like that, you know where to go, which is ScrollView.

So, we're gonna talk about the basics of ScrollView.

Kind of how to get a ScrollView to work.

First, let's talk about how we add subviews to

a normal UIView.

So, I got a normal UIView here.

This white view in my iPhone here.

And I wanna add a subview to it.

And so, you know, from Lecture 5, I think, that I just create

the frame which positions it and then I say add subview.

Okay, so I've got this view called Logo.

And it's just a UIView.

An image view, probably.

And, so I just add subview and, boom, it appears.

Simple as that.

It's really easy.

So, what is the analogy to doing this

for ScrollView, okay?

Let's go look at that.

So, in a ScrollView, if you wanna add a subview.

The most important thing you have to do is talk about

how big the area you wanna scroll over is.

So, you set this property in ScrollView called contentSize.

So, here I'm gonna set my contentSize to 3000

wide and 2000 high.

So, I'm gonna scroll over this huge space, okay?

Once you set the contentSize,

now you can add subviews just like you do a normal view.

So, here I got logo frame.

I'm gonna set the frame to way over on the left there and

I am going to add its subview.

So I'm adding that subview to the ScrollView.

But instead of it appearing onscreen there,

it's just kind of placed in this content area.

And so, I could add another one.

Here's a huge image, aerial view of Stanford.

And so, I'm gonna set its frame and

add it as a subview, okay.

So this is the fundamental,

this is all there is to ScrollView.

It's really simple, actually.

It confuses people for some reason, but

it's really just this simple.

You set the contentSize,

then you add subviews to the ScrollView.

Now, the ScrollView is just going to

be a window on that content area.

You see, it's just gonna scroll around,

looking in that content area.

And it'll see all the subviews as it

moves around in there, okay?

So, that's it.

That's all there is to it.

ScrollView is actually incredibly easy to use.

Now, you can reposition the views, anytime you want,

just like you could if they were subviews.

You can just change their frame and they'll move around.

Or change the logo's frame, move it around.

I can also change the contentSize.

[NOISE] And now it zooms down.

I can set the contentSize to be

the size of the biggest subview.

And now, again,

when I scroll around, it's just zooming around, okay.

So it's very common in the ScrollView,

obviously, to set its contentSize to be the size of

the biggest subview.

So that it exactly contains it.

Okay, now, as this thing moves around, you might wanna know,

where is it, okay?

What we are currently looking at in my content area?

And you do that with the property in

ScrollView called contentOffset.

And it's simple.

It's just an x-y offset from the upper left,.

Of the upper left of the,

you know, bounds of the scroll view.

Okay, now what about the actual rectangle

that's showing?

What is that?

That is actually the scroll view's bounds.

Okay, it's very important to understand that that is

just the scroll view bound, that yellow rectangle I'm

showing you there, that's the scroll view's bounds.

But if you want to know what that rectangle is

in the content, like, in that aerial view,

what is that rectangle, then you have to convert it.

You have to convert the scrollView's bounds using

convertRect, [COUGH] which is UIView method.

You have to convert it to that,

to whatever view you wanna convert it

to like the aerial view whatever that aerial view is.

Does that make sense?

So that rectangle is the scroll view's bounds.

Okay?

Questions?

All right. That's easy.

So that's it.

That's pretty much it to setting up a scroll view and

knowing where the thing is scrolling.

Couldn't be simpler actually.

How do you create a scroll view?

There's actually numerous ways to do it.

You can drag one into your storyboard.

That's what I'm gonna do in the demo.

You can actually select a UI view in your storyboard and

go up to the menu up there,

and say embed in scroll view, just like we did embed in

navigation controller in a previous thing.

You can of course create Scroll Views in code.

They're just views, so

you can write a UIScrollView frame whatever.

And boom, you've created a Scroll View.

So, they're just normal views.

And then the code if you wanted to

add those subviews in code would look exactly kind of

like what was on the previous slides.

Which is we might create an image view here,

the green is just creating UI image view,

and then I'm just calling addSubview.

But if you just do this, these three lines of code,

it won't work because you have to set the content size.

If you don't set the content size,

it starts out zero, until you bring your Scroll View up,

and they'll be nothing there.

Because you won't have that big white area to scroll

around in.

Okay?

So no matter what Sub Views you add,

if you don't have that big white area defined,

then Scroll View will not be showing anything.

Okay, so it's as simple as that.

This is a review of what I showed you graphically.

Now you can scroll around.

The user scrolls around by just using their finger.

Right? They pan around.

But you can scroll programmatically too by saying

scrollRectToVisible and this will take a rectangle in the

content areas view coordinate system, that content size.

And it'll scroll the Scroll View to be showing that.

And animated is whether it slides over.

If you don't say animated, it's just gonna jump there.

Which is kind of abrupt to the user, if it's on screen.

And there's tons of other things you can do

with a Scroll View that I'm not gonna really talk about.

But you can lock the scrolling direction, so

it'll only scroll up and down, or only scroll left and right.

It'll flash the scroll indicators if the content size

has changed.

You can control that.

There's a whole bunch of stuff you can look at

UIScrollView in the documentation.

Okay?

I'm going to have one thing important about

scrollview which is zooming.

Cuz so

far I just showed you kind of moving around and of course in

scroll you can also zoom in on the content area, okay?

Zooming in requires you to do a few things though.

One thing is you have to set the minimum and

maximum zoom scale.

So the zoom scale is just how much you'll zoom in.

So, if you have a zoom scale of 2.0,

then that means that you'll only zoom to twice the scale.

And if you're 2.5, it'll zoom down to half of whatever

the normal size is of the thing you're looking at is.

So you have to set these.

If you don't set these they'll be default to 1, and

it won't work because the scaling will go from 1 to 1.

Okay? And so

that means not scaling at all.

The other thing you have to do is use a delegate.

So here's the second time I've showed you a delegate.

Now I had you do all that work with a calculator to

do the delegate so that you'd be

ready to do things in iOS that use delegates.

And here's one.

'Kay. You've set yourself as

the ScrollView's delegate, and you implement this method.

You have to implement this method if

you want zooming called viewForZoomingInScrollView.

So the scroll view when it's zooming, it only zooms in,

in the coordinate systems of one of its views.

So usually it's kind of the background view.

Like in that aerial example,

it would be the aerial view that we would zoom in, okay.

But we're zooming the whole contents, so

everything gets zoomed,

but that's gonna be the view that you're zooming in.

So you have to tell it which view that is.

It's usually pretty obvious.

Often times in ScrollView,

you're only scrolling on one view anyway but

you still have to implement this, okay.

You can zoom programatically once you've set those two

things up properly.

Then you can set the zoom scale, how zoomed in you are.

It looks kind of like this.

So, here I've got a picture.

Let's say my zoomScale is 1.2.

I go to zoomScale 1.

I zoom out, zoom back to 1.2 zoomed in.

Okay? And same thing with zoom rect.

So if I had a little rectangle like that yellow area and

I said zoom to that rect, it would zoom to make that

rect fill the edges as much as it could.

Or if I have a rect that was really big, and

I said zoom it would zoom it down to fit.

Okay?

So, zoomToRect means make this rect kind of either go big or

go small to fit.

Okay?

All right?

There are tons of other delegate methods in

scroll view.

One thing, when you start using scroll view,

you're going to ask me is,

hey, scroll view has 12 delegate methods.

And when I say I implement the UIScrollView delegate

protocol, how come the compiler doesn't complain and

say that it will implement those 12 methods because I

thought if I said I implemented a protocol,

I have to implement all those protocols, okay?

The reason that that happens, that you don't get

a warning there is because there is actually, for

historical reasons, a different kind of protocol,

which is an Objective C style protocol.

Objective C was the old language that iOS

was written in and in the Objective C style protocols

some methods in the protocol were allowed to be optional.

Now that's not true in Swift,

but that's true in Objective C.

So all these old delegate protocols like

scroll view that have optional methods in them,

Swift knows how to bridge them.

You can actually create your own Objective C compatible

protocol with optional methods by saying @objc in front of

your protocol declaration.

So where you would say protocol face view delegate or

face view data source you would say

@objc space protocol, whatever delegate, and

then you could put optional in there.

And you can read,

this is all covered in the reading assignments, so

hopefully you've got that.

But that's the magic that makes it so that when you say

you implement the UIScrollView delegate protocol and

you don't yet implement all the 12 methods in there.

In fact you only need to implement the ones you

want like view for zooming and scroll view.

That's why that works.

So anyway there are a bunch of

other delegate methods in there.

For example scrollViewDidEndZooming.

So that will be sent to you when the user pinches and

then they lift their finger up.

'Kay? And it'll say oh,

the user has finished zooming and

this is the scale they left it at.

And you, at that point, might want to redraw your view to

be less grainy, or something like that.

If you do withdraw your view at the new scale,

you want to make sure you reset the transform.

Okay. This property on

view called transform is an Affine transform.

Really that's all scroll view is modifying.

When you pan around and

zoom in, it's just modifying the property transform.

So, you can go look at the documentation for transform in

view to understand what's going on there.

Yeah, it makes scroll view really easy in that it

just is modifying this transform.

All right, so that's scroll view.

So, I have a demo of scroll view.

We're going to learn some other

things along the way while we're doing that.

Okay, let's do that.

Let's go over here.

We're gonna start from scratch again.

So I'm gonna create a new Xcode project.

'Kay. It's gonna be Single View.

Those of you who like to follow along are not gonna be

able to today.

Sorry.

Because I'm going to be dragging in a file with some

URLs in it.

I'm going to call this thing Cassini.

That's going to be the name of my app.

And I'm gonna put it where I always put everything.

All right, I'm not going to be doing anything with the app

delegate as usual.

I'm not going to be doing image assets.

I'm not going to do the launch screen.

So, here I just have a simple storyboard.

I'm gonna also get rid of

all this ViewControl life cycle here.

I don't need that.

Get rid of that.

And so here, simple storyboard only has this one

view controller here called ViewController.

And I'm not even gonna pay any attention to

this view controller.

Actually, I'm gonna create a new view controller.

I'm gonna drag it out here and this new view controller,

its only job in life it's going to display an image.

So I'm going give it the URL of an image, a JPEG file or

whatever, and it's going to display it.

That's going to be its job in life.

So I need a new view controller for that.

So I'm going to call this new view controller.

All right? I'm going here,

Cocoa Touch Class, it's a view controller.

I'm gonna call it ImageViewController.

Cuz that's what it does, it displays an image.

Okay? Very, very simple.

I'm gonna put it where all the rest of my stuff goes, there.

All right? So here we go again.

Again, I'm gonna get rid of my

view controller life cycle there.

We're gonna actually add some of those back,

cuz part of the reason I'm doing this demo, also,

is to show you a little bit where and

why, how we would use a view controller life cycle.

So, my ImageViewController, one other thing,

let me make sure in our story board here, we got this thing,

I'm gonna make sure I change the identity of it

to be an ImageViewController.

So this is an ImageViewController.

This is an easy thing to forget, but do that.

Okay, so what do I need in my ImageViewController.

Well, I need a UIImageView.

We saw that in the Auto Layout demo.

It's a just a view, kind of like a UI label for images.

Obviously if I'm going to have ImageViewController,

I'm going to need one of those.

Now, I could just drag it out into my story board, but

I want to use this demo as an example to show you

how you create a view in code, create and use a view in code.

So instead, I'm just gonna have a private var here.

I'm gonna call it imageView.

And it's going to be a UIImageView.

In fact, I'm just gonna create it right here.

Okay.

So I have this image view.

I'm just calling it there.

Notice, I'm not using the frame.

So it's going to create this image view.

It's going to be zero, zero, by zero, zero.

It's going to be, it's framed.

It's going to be, now it's not in any super view anyways, so

it really doesn't matter what its frame is yet.

Okay. It's obviously going to

matter a lot once we start putting images in there.

I'm also going to create a little private var

here called image.

And this is going to be a computed var.

I did this in Auto Layout as well.

It's going to be a UIImage.

And all I'm going to to here in the getter,

I'm just going to return the imageView's image.

And in the setter,

I'm basically just going to set the imageView's image to

the new value.

Now, why do I do this?

'Kay, I'm doing this for

the exact same reason that I was doing it in the other one,

which is that when the image is set in my imageView,

I want to be able to do things, I want to intervene.

So I basically I want to kind of be able to do

the did set kind of thing to make that work.

So, what's my warning here, UIImage.

Okay, so here we go.

So in other words right here, when this is set,

when this image gets set.

Setting this image just is going to set the image in

the imageView, that's all it does.

But I can put other code that I

want down here because I'm going to

need to do some things every time my image changes.

I don't care about getting but every time my

image gets set in my imageView I need to do some stuff.

Let me see.

Well, let's talk about what some of those things might be.

For example,

when I set it I probably want to do imageView.sizeToFit.

Okay. So when you change the image

that's in image view, this is kind of like saying label.text

equals something, right, I'm changing the imageView that,

the image that the imageView's showing.

When you change it, it does not change the bounds or

the frames of the image view.

So, sizeToFit causes it to.

So ImageView looks at the image, sees how big it is and

expands its frame to fit.

So that's clearly something I'm going to do every time I

change the image I want to do that.

So now you can see why I have this nice little convenience.

You can kind of think this is similar to

having a function called setImage.

But it's just nicer to do it as a property cuz then I

get set and get and my code looks really nice.

'Kay, so there's that.

I'm also going to do viewDidLoad, okay?

So viewDidLoad, we know that we of course need to call

super, and what I'm gonna do in viewDidLoad?

Well, I'm just gonna add my imageView to

the view hierarchy.

Okay, I'm going to plop it right here.

Again my storyboard, got this thing,

got my view as a top level view.

I'm just going to add my view in here.

Now, I'm not doing a scroll view yet, okay.

I'm going to do it without a scroll view, so

we can see what that looks like.

So I'm just gonna say view, addSubView, my ImageView.

Okay, and we know that this is fine to do because

viewDidLoad happens after pretty late in the process.

Now, I'm doing anything here that's geometry based.

I don't set any frames or anything like that, so this is

just putting the ImageView in there with whatever frame it

has, either from the last sizeToFit that it got or

it's still zero, zero because it's never been set.

Okay.

So I'm just adding it as a subview there.

Now, let's talk about what the model is in this

view controller.

What is it's model?

Look for the model right at the top here.

It's model is going to be into public.

It's going to be a public resettable model,

which is an image URL.

And it's just going to be an NSURL, so an NSURL is a class

that holds a URL, like http// some Stanford.edu something

and that's going to be the model, and every time we

set the model it's going to get to that image and

put it in the view, okay?

Everyone understand our nice model here.

It's a very,

this is a very well designed MVC it has a very clear model.

It's clear what it does in its view.

So one thing that whenever our model is set,

though, we want to make sure that we update our UI.

So when our, if we set a new image URL I'm immediately

going to set whatever existing image we have to nil.

Because we're changing it,

and then I'm going to call a function fetchImage.

Okay? And

fetchImage is just going to fetch the image that's in

the URL, and load it up into my image view.

So, I need this private function fetchImage, okay?

So let's talk about how we get an image from a URL.

If someone give me a URL, how do I get the JPEG image or

whatever that goes along to it?

And it turned out to be quite simple.

First, I'm gonna make sure that my image URL is not nil.

If someone asks me to load up nil,

I'm not gonna do anything.

So I'm just gonna basically ignore that.

So if it's not nil, so my URL is not nil, I'm gonna go and

go out to the Internet, or if it was a local file,

it would be the local file, and get the image data.

So I'm gonna say imageData equals, and

I can do that with the class NSData.

And you'll remember NSData from a long-ago lecture,

maybe lecture three or four.

NSData is just a bag of bits.

Right? So I'm gonna reach out on the

Internet and grab the bag of bits, which is this JPEG file.

And I do that by using contentsOfURL initializer

here, and I just give it the URL.

So I can go out and get that.

Now this could return nil.

What if I just gave it some bogus URL, 'kay?

There's no file there.

So let's check and make sure this is nil.

So I'm gonna say if the imageData does not equal nil.

Why do I keep getting the caps lock there?

I'm not really sure what I'm doing there.

ImageData does not equal nil,

then I can do something about it, else, I can't.

So I could do if let, by the way, here, but

I'm doing it this way for a reason.

You'll see a little bit later.

So if the imageData's not nil, what am I gonna do?

Well, I need to turn that imageData, that bag of bits,

into a UIImage.

UIImage is the class in iOS that represents an image.

So that's really easy to do.

I'm just gonna say,

in fact, I'm just gonna assign it to my image.

I'm sorry, we don't need self there.

I would say image equals UIImage with the data

being that imageData.

Okay?

Now, otherwise, I'm just gonna say my image equals nil, 'kay?

And, remember, this image equals something like this is

just calling this right down here to

set the image in our imageView or whatever, okay?

And then, of course, this needs to be unwrapped.

But we know we can unwrap it because I

checked to make sure it's not nil.

Okay, now, this line of code is a very bad line of code for

us because it might go out over the network.

Okay? I could be on cellular.

This might take ten seconds, or a minute, or it might just

fail because I have no network connectivity right now, okay?

So this is not a line of code we wanna be executing in

our program, but

we don't know what to do about it right now and I'm gonna

show you later in the lecture what we can do about it.

So, for now, we just got this kind of bummer line of

code here that's gonna be really slow.

Our app is gonna be really slow.

Although the network at Stanford here is so

fast that it probably won't even seem that slow, but

it will be slow cuz I'm gonna grab some pretty huge images.

All right, there's one other thing I wanna show here in

terms of view controller life cycle, though, which is,

if someone sets my imageURL, okay, and

I'm not on screen, do I really want to go and

start using up their cellular data plan, okay?

Probably not, 'kay?

You gotta remember that people don't have Stanford WiFi

internet everywhere.

Some people are using cellular, and it costs money.

So I am not going to actually fetch this

image unless I'm currently on screen.

So how do I tell if I'm currently on screen?

Actually I can do that with view.window does not

equal nil, 'kay?

So remember view is our top level view in our

controller's view ,and .window is the UI window it's in,

which will be nil if it's not currently on screen, 'kay?

But there's a little bit of a problem here,

which is what if I'm offscreen and someone sets my imageURL,

I don't fetch the image, and then I come on screen?

Aah!

Then I gotta fetch the image, 'kay?

So where does that happen?

Anyone wanna hazard a guess where I would put that code?

Nobody?

ViewWillAppear, exactly, very good.

So in viewWillAppear down here,

I'm gonna say super.viewWillAppear.

And then I'm just gonna say, if my image is nil,

then fetch the image, 'kay?

Now it could be that my image is nil because I

tried to fetch it and it failed, but

it's just gonna try again and fail, that's okay.

But, obviously, my image is not nil,

then I don't wanna fetch it again.

That would be wasting their cellular data even more, okay?

So here's a classic example of where we

would use viewWillAppear,

because we know it's worth it to go get that data,

because we know we're going to appear onscreen, 'kay?

Everybody understand that,

view controller lifecycle coming to life for you?

Okay?

So that's good. So let's go ahead and run.

So you, actually, let's go ahead and

put a default image in here.

I actually have some URLs.

I'm just gonna drag this DemoURL.swift in here.

And all this DemoURL.swift does is it just defines

a bunch of URLs that we're gonna use in our demo today.

So one of them is Stanford, right here.

So I'm going to load that up.

So in my viewDidLoad, I'm gonna say, if my image is nil,

then my imageURL equals my DemoURL.Stanford, 'kay?

So I'm just gonna,

in viewDidLoad, I'm gonna set my imageURL.

Now, it's not gonna actually load this until it

will appear.

That's okay.

But I'm just setting it right here, 'kay?

And I'm not, this is really for testing anyway.

I probably don't even need to do that just cuz we're gonna

remove this code once we start really not testing and

actually working.

So let's go ahead and run this and see what we got.

Oh, so it's not gonna work.

Okay, so this comes up, it's blank.

Why is this blank?

Well, let's look at our storyboard.

Here's the entry point to our storyboard.

It's this blank view controller.

Anyway, we don't want that.

Let's move them over here and have it

come into our view controller image view controller here.

Oh yeah, there we go, we got Stanford.

That's very nice, but can't scroll around, 'kay?

Oops.

I can't zoom, can't turn the pinch, nothing.

Okay, so I can't see it, 'kay?

Can't see anything.

So that's, obviously, we need a scroll view, all right?

So, let's go ahead and

first of all, actually, what should we do first here?

Let's do, let's enhance our user interface a little bit

more so we can pick other images.

I don't wanna just have this Stanford image, I wanna have

some different images that we can look around in.

So I'm gonna go over here and

drag out a split view controller.

Where's that split view?

Right here.

So let's drag out this splitViewController.

And, as usual, with the split view controller, I'm going to,

when I zoom out a little here, we can look at it.

I'm gonna delete a lot of the stuff that came with it here.

Keep that one.

We'll delete that.

I'm gonna have,

my image view controller is going to be the detail, 'kay?

So I made the detail.

I'm gonna make my entry point here be my

split view controller.

And I'm gonna have my master be this little one that was in

here before.

By the way, I just Ctrl+drag there and set this little view

controller that was there when we first started our app,

this blank view controller.

I can set it as the root view controller of

the navigation controller with Ctrl+drag and

pick root view controller here.

In your, in the demo and in your thing, we, you probably

did this in Editor > Embed In > Navigation Controller, but

you can also do it with Ctrl+drag, 'kay?

So now I have a nice UI here.

Comes up, shows my nice image view right there.

If I go back, here's my master.

So now, in this master, I'm gonna put some buttons so

we can pick different images to look at instead of

just looking at that Stanford thing.

So let's do that, really easy.

I'm just gonna grab some buttons here.

Let's do this.

Let's make the buttons really big,

maybe like 48 point, okay?

This button bigger.

And let's make three of them.

'Kay?

One of them is gonna be a picture of

the Cassini satellite.

Okay, that was sent out as Saturn.

Another one is gonna be a picture of Earth.

Another one is gonna be Saturn.

Okay?

So let's do a little bit of our favorite thing here,

which is auto layouts.

So put that one right in the middle.

Put that one right underneath.

This one right on top, 'kay?

I'm gonna try and reset to suggested constraints and

see how that works.

That didn't work very good, let's undo.

I think that's because,

let me make this thing be its normal size, that's why.

There we go.

'Kay?

Put these back.

Hopefully, this will, there we go,

Reset to Suggested Constraints.

Oh, still didn't work.

Okay, well, we'll fix them up, manually.

So this guy, right here.

oh, that's so bad, it's not even close.

Okay. So

I'm just going to do this all manually here,

going to uh-huh, sorry, wrong thing there.

Okay, Ctrl+drag, let's make it center vertically.

Let's Ctrl+drag and center horizontally as well.

Let's make this be vertical spacing.

We'll make this one be vertical spacing we'll have

these things share their center x, center x.

I think we're good to go there.

Yep, frames.

So, let's update our frames, less time.

Okay, there we go.

All right, so we've got some nice auto layout there, and so

now all we need to do is to segue when these are pressed.

We're gonna segue to our Image View Controller down here.

So let's set that up and just Ctrl+drag.

nope, Ctrl+drag, all right.

So I'm gonna show detail because in

the split view I want it to show up on the detail side and

on iPhone it's gonna push,.

So there's that one, let's go ahead and set its segue.

So this one I'll just call it Earth, 'kay, this one,

down here, show detail we'll call this one Cassini, and

with this one, show detail, we'll call that one Saturn.

Okay, so there we go, we've got a nice story board here,

we've got these segues all set up in here all

we need to do is prepare for these segues.

This View Controller right here, okay,

I'm going to select on this and go here to see its code.

So here's the code for that View Controller.

It's just this blank one that got created when we

created our app.

Okay, and all I need in this ViewController is

prepareForSegue, nothing more, so I'm gonna prepareForSegue.

Actually I'm going to make

this quite a bit wider here with these huge fonts.

And in here I'm going to see if it's a segue to

an image ViewController.

So I'm gonna say if I can let image view controller equal my

segue's destinationViewController as

an ImageViewController.

Okay, so we know we're segueying into

an image view controller.

Then if I can let the identifier equal

the segue's identifier, so the identifier's not null,

then I'm gonna switch on the identifier.

This code should all look very familiar to you,

and then in case of Earth, 'kay,

we are going to have to set the image URL, 'kay?

So ivc is an image view controller,

its public settable model is the imageURL.

So I'm gonna set that to DemoURL.NASA.Earth.

'Kay, I have a nice one ready to go there.

I'm also gonna set the title of that to Earth,

'kay, and then same thing here for Saturn.

It's image view controllers,

imageURL equals the DemoURL.NASA.Saturn.

ivc.title equals Saturn, and same thing for Cassini.

Let's see.

'Kay, and otherwise we will just break, and

do nothing, okay?

Everybody understand this code?

Hopefully it's completely familiar to you.

Nothing special there at all.

Okay, so I think that should work.

Let's try it.

Hopefully it didn't clean anything.

All right, so we go back, we've got these things.

Let's go ahead, oh,

my auto layout doesn't look very good there.

Probably messed that up, but anyway, close enough.

Let's go Earth, oh, it didn't work, okay?

That's because we have back here,

here in our engine controller, we have this test code here.

Let's get rid of that, right there.

Try again.

Right back now, let's look at Earth, and

look how slow this this, you see what I'm talking about?

Like, hello,

can't do anything, can't click on any buttons.

Finally, we get this and this does not look like

the Earth to me, looks like a bunch of dim photos.

I don't know what that is how about Cassini?

oh, we got the outer space part of Cassini right, but

that's all we can see because we need the scroll view now.

We absolutely have to have the scroll view.

These are big images so we can not see anything without it.

So let's go back and put our scroll view in here.

All right, so how are we gonna put the scroll view in here?

Well, I'm actually gonna put the scroll view in

using the storyboard.

So, I'm gonna drag a scroll view in.

That's kinda a simple way to do it.

So, let's bounce out here so

now we're back to our Image View Controller.

All right, this is our Image View Controller, and

I'm just going to drag out a scroll view.

I'm gonna search for

it, scroll, there it is, drag it out here.

I'm gonna place it so that it's right on the edges with

the blue lines right there.

I'm definitely gonna do Reset to Suggested Constraints.

I'm gonna make sure that works, which it did,

'kay, no magic numbers.

Everything looks nice right there, and now I

just need to put my image view inside this scroll view right.

So I got this scroll view here all I wanna do is put

the image view inside of it.

So how am I gonna put that imageView inside of it.

That's very, very, very simple I'm just going to

create an outlet to this thing.

Let's put the outlet right here.

Okay, I'm going to call the outlet scroll view, and

as soon as that outlet is set

in it's did Set I'm going to simply say to.

The scroll, well actually, we, you know,

we already added the scroll view.

Let me make this a little easier on you.

See right down here where we add the image view to

our view?

Instead, I'm just gonna add it to the scroll view, 'kay?

So you can build your view hierarchy in view did load.

It's actually a pretty good place to build it,

cuz all your outlets are set, so we know this is set, and so

we can build our view hierarchy here, 'kay?

So I just added the subview to there bu I do actually need

this didset here.

I need again to make this wider so

you can see it because when I set my scroll view what's

the most important thing to make a scroll view work?

The content size, right.

So when my scroll view gets set,

I need to set the content size, so

I'm gonna say scrollView.contentSize, and

what size do I want it to be?

Well I want the content size to be exactly the same size as

the image view, so

I'm just gonna say imageView.frame.size.

'Kay, now that's not, oops, equals.

That's not the only place I need to do this, though, 'kay?

Any time my image changes,

obviously my image view might change,

I need to change the content size of my scroll view.

So also here.

Inside the setter for image I need to set my scrollView's

content size equal to the imageView's frame size.

Here I'm going to do something kind of interesting,

which I actually did in auto layout demo after the fact

when I posted the code, but

I'm going to put a question mark right here.

And the reason I'm gonna do that, it allows me to set my

image internally, even if my outlets have not been set yet.

I know that these outlets are implicitly wrapped optionals,

and of course that means that you can access them,

but the reality is you need to protect against the cases

where those things might me nil.

Like setting your image,

somehow, before that was your set.

So, now I don't need to do it here,

because I'm in viewDidLoad.

And I don't need to do it here because, of course,

it just got set.

But here, I'm going to do it just to be safe.

Everyone understand that?

Okay.

So, let's see.

It should work.

All right here we go, let's go try Cassini.

Slow, okay.

Oh yeah, look, oh stars.

Oh, what's that?

Oh, look at that.

Oh, sorry.

There it is.

Oh, oh,.

It's a spacecraft of some sort.

Hard to tell, kay?

We don't really get to see a lot of it, but

[INAUDIBLE] Cassini.

Okay, well obviously we need zooming, kay?

Because I can't see really what's going on here.

All right, so let's go back and add zooming.

Again, very straightforward.

Remember, there's only two things you really need to do.

You gotta set that min max, and

you gotta implement that delegate method.

Great place to set delegate methods, of course,

is in the setter of outlets.

If you wanna set the delegate of an outlet, so

I'm gonna set the scrollView's delegate to be myself.

Of course I'm gonna have to say that I implemented.

That's the problem here.

Okay.

We'll do that in a second.

While I'm here I'm going to set the minimum zoom scale

to 0.03.

This is a very,

very large image that we'll be able to look at here.

And the maximum zoom scale to 1.0 and

maybe these don't wanna be magic numbers.

Maybe these want to based on the actual size of

the imageView.

Okay?

So you might want to make it so

that you can perfectly zoom out to fit the scrollView or

something like that.

But I'm just picking numbers here to

make this code simpler.

So let's go fix the fact about the delegate here.

So I'm just going to go up here and say,

oh yes, ImageViewController is a UIScrollView delegate.

And, as soon as I say that,

this warning went away down here, but

I didn't get a new one that said, oh,

you don't implement this.

And again, cuz this is the old objective C

style protocol where there's optional methods.

So, I'm going to implement one of

them though which is view for zooming in scrollView.

Okay, as soon as I typed viewFor,

it knew what I was talking about.

And I'm just going to return my imageView because that is

the view that's inside the scrollView's content area that

I want to zoom on.

And that's it.

That's all we need to do, so let's go run.

Now we should be able to go and

take a look at Cassini in all of its grandeur.

I'm just going to hold down Option so that I can pinch.

And I'm just pinching in here.

Oh there it is. And let's go ahead and

rotate the other way.

There we go.

So we can see it and zoom out a little bit.

We can still scroll in addition to zooming.

Okay? And let's go look at

this Earth thing so

we can figure out what the heck is going on with that.

So slow.

Okay, so here it is.

Oh, now we can move around and look at things.

Let's zoom in on this a little.

[INAUDIBLE] oh, whoa, it's the Earth.

Woo, okay.

So, yes and as we can imagine, guess what this one is.

Pictures of people.

So this is actually made up of composites of images of

people waving at Cassini.

Kay they're taking pictures of themselves waving, see?

Hello Cassini.

So that's what this composite is made of and

then I'll push in that Saturn out of there.

'Kay so.

NASA, pretty cool.

All right.

Okay, so that's good.

So that's all I'm gonna show for now.

We'll get back and work on the fact that our app is really,

really slow in a minute.

Gotta show you some stuff first though.

'Kay, any questions about that?

ScrollView's really easy to use, but you just

gotta know these particular things like the delegate and

that min max zoom scale and that content size.

As long as you do those things,

it'll work really great.

All right, so a little aside here.

Just a two minute aside.

I'm gonna rip through these slides about closures, but

there's an important aspect about closure that

you need to know.

Kay?

Closures, when you have a closure in your code,

it captures any variables that are used inside

the closure from the outer scope, where the thing is,

which is a really cool feature.

That's what makes it a closure really, okay.

Is that it captures them, okay.

You can make incredibly elegant code out of

this, okay.

Now here's where I'm going to

show you something about the calculator that if you

didn't do this in your assignment, that's good.

Because in your assignment I wanted you to

use delegation, okay.

However, sometimes closures can be a substitute or

a better tool to use than delegation.

Lets look at this code right here.

Lets say I have a class called grapher.

I'm not going to say what kind of thing it could be

because some of you haven't turned in your homework yet,

but lets say I had a grapher, okay.

And it doesn't have a delegate method or anything it just has

a var called yForX whose type is a function.

A function that takes an x and returns a double.

Okay?

Now that's an optional function.

That's fine, doesn't have to be set in this grapher.

And then what if I just said,

well I'm going to let the grapher equal a new grapher.

I'm going to let variable graphing brain be

a calculator brain instance I just created.

Then I'm gonna set the program of the graphingBrain I

just created to some other program that I wanna graph.

Then I'm just gonna say yForX equals a closure and

inside the closure it just says,

set the variable value to the argument to the closure and

then return the graphingBrain at evaluate.

So do you see how by using this,

first of all we have grapher is completely generic.

Knows nothing about a calculator brain.

And yet we have this five line,

six lines of code to implement our graphing and

it's all incredibly easy to kind of parse what's going on.

Now this works because the closure,

that little thing in the curly braces,

captured the little graphing brain that I created.

Okay, so that every time disclosure is called,

every time, you know, grapher one to yForX.

It keeps reusing this graphing brain over and over and over.

Which is exactly what I want because this

graphing brain's got the right program in it.

It just keeps calling it over and over.

It doesn't create a new one every time, it just keeps

reusing it because it captured it from the outer scope.

Everyone understand what I mean by capture?

Now, this is really cool and really elegant,

it looks really awesome, it is awesome, but

there are some dangers.

Okay?

Capture danger.

The problem is, if a closure captures a pointer to

something and that something has a pointer back to

the closure, now you have a loop.

A memory cycle that's going to keep them both in

memory forever.

Okay? Kind of

like the memory cycle we were talking about before.

This is easy to have happen in a closure.

This is what it looks like, so

here's another example here of closures.

I've got this class called Foo.

It's got one property which is a function called action.

It's got another function called show,

which takes an integer and shows it, prints it.

And then it's got a method called setupMyAction, and

setupMyAction has a local variable, x,

which is an integer.

That it assigns that action,.

[COUGH].

Function, property to a closure.

Inside that closure it uses the x.

That's cool, it captures x.

This will actually work.

This will print 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.

You can see the doMyAction10times at

the bottom.

If you set up my action it will do it because the x

gets captured by the closure and

every time the closure get executed.

[COUGH]. It increments that x and

counts to 10.

It's also cool because it's capturing self.

Because I'm using myself to show the number,

by calling self.show.

But here's where the danger comes in.

Okay?

Because self has a pointer to the closure and

the closure inside has a pointer back to self.

Now, when you write a closure you'll notice that any

time you call methods it makes you put self,

makes you say self dot.

That's because it wants you to be clear.

You are capturing self when you call show.

Okay?

Now, this is a problem, this class

Foo and this closure are pointed to each other, they

will stay in memory forever, they can never be released.

How do we fix that problem?

Well, we can actually say,

when we make a closure, do not capture this variable in here.

Okay?

You do that by saying, [COUGH] unowned,

whatever the variables you don't want to be captured.

Okay? You just put it right at

the beginning of the closure, before the in.

Okay? If you have arguments,

you put it right before the, in, there.

That will make it work.

You can also say weak self.

Which is a little different.

That will make the self be a weak pointer in there.

We learned about weak pointers in the lecture when we

talked about delegation.

So weak would work, too.

It wouldn't do it.

The difference between unowned is that pointer doesn't have

to be an optional that can be set to nil.

It can just be anything.

You just guarantee that self will never become nil.

So, for the lifetime of this closure,

self can never become nil.

Okay?

Now, self can be released from the heap,

which will release the closure now, from the heap.

That's great, but

that closure can never be executed while self,

this Foo thing, is nil.

Which is fine, because it's only used inside Foo,

[COUGH] That would never happen.

Okay?

Unowned self.

This is all covered in the documentation that you

were supposed to have read, but, you might have

not really understood this, or glossed over it, so I'm

just revisiting it to tell you that [COUGH] It's important.

Okay. Because we're going to

start using closures more and

more in this class and you will be capturing values and

some of them will point back to the closure.

Okay?

[COUGH] Okay, that's it.

Next is multithreading.

Okay. Yes, question.

Is there an example, could you give

an example other than self where it would point back?

I think about that swift reference document has a whole

big, long example, so I would just refer you to that.

Any other questions about that?

[COUGH] Okay.

Multithreading, how many people in here have done some

programming in a multithreaded environment before?

More than half, that's more than usual, so that's good.

All right, then this will all be pretty easy for

you to understand.

Those of you who haven't, it will be a struggle.

The first time you do it, I'm sure all of you can attest,

it's a little bit harder to understand what's going on.

It's actually fairly simple.

What's going on here is, in iOS there are multiple queues.

A queue meaning like a queue at

the movie where people lined up to go see a movie.

A queue in a computer science sense is just a list of things

where things go into the front of the queue, go into the back

of the queue rather and they get pulled off the front.

It's a queue of functions, basically closures usually.

Okay.

That are sitting there waiting to run.

Each of these queues has it's own threads to run

these queues, to process the things on the queues.

That makes this a multithreaded environment.

Now, there's a very,

very important queue called the main queue.

This main queue is a serial queue, meaning it

pulls these functions off the queue one at a time.

It never runs [COUGH] Two of them at the same time.

Okay. They're serial, one at a time.

And all UI activity has to happen on the main queue.

So if you ever wanted to have a function or closure,

that is executing some code, that did anything with the UI,

you have to put it on the main queue.

It can only execute on the main queue.

This is how basically the UI is protected

from getting messed up in the multithreaded environment.

It has this main queue.

Okay? So the main queue,

very, very important.

The main queue never wants to

be doing anything that might block NS data contents of URL.

That should never be in the main queue.

So we're going to have to get that off the main queue.

All the code that you write normally in

your NBC that's all happening on the main queue.

View did load, view will appear, all that stuff,

all happening on the main queue automatically for you,

you don't have to do anything special.

Okay?

But, there are other queues that iOS will create for you

as you need, and we're going to show you how those work.

First of all, how do execute a function or

a closure on another queue?

Well, you do that with this function [COUGH] Called,

dispatch_async.

Okay?

So you say dispatch_async, the argument,

there's two arguments, a queue and a closure.

The first argument is the queue.

I'm using the trailing closure syntax here to

put the closure outside the parentheses there but

you just specify what queue you want to

execute the closure on and then you pass it to closure.

Okay? And the closure takes no

arguments, returns no arguments and that's it.

That's how you do it.

Couldn't be easier.

dispatch_async on the queue you want.

So how do you get the queue that you want?

Well, you get the main queue using this function

dispatch_get_main_queue and that returns the main queue.

There's also an object-oriented way to do it,

NSOperationQueue.mainQueue.

You got this queue and

here's an example of putting something on the main

queue dispatch_async, [COUGH] notTheMainQueue,

something, and then inside there [COUGH] You could

say dispatch_async_main_queue.

This is how you do multithreading programming.

Those lines of code right there.

I want to do something that doesn't block the UI, so

I dispatch it to notTheMainQueue,

some other queue,

[COUGH] I'll talk about how to get those queues in a second.

Then I do it, go do something like NSdata contents of URL,

blocks, whatever.

When it's done, I need to talk back to the main queue to

update my UI with the image I got or something like that.

The way I do that is [COUGH]

I dispatch back to the main queue.

So you nest these calls to the dispatch.

Okay?

You're basically running code on another queue and when

it wants to dock back to the main queue it just posts or

puts some code back on the main queue.

Now, the main queue might be busy.

But when it's done it's a serial queue but

eventually it will get to this block and it will run it.

Okay?

Everybody understand that?

That's it.

It's so simple really, actually when you see it,

it's almost too simple.

The code, when you write it, it's so simple you

can get confused and forget that you're in a multithreaded

environment, but this is really, those lines of

code you're seeing right there at the bottom, that is

the fundamental multithreaded programming syntax in iOS.

How do you get these other queues,

you don't want to run on the main queue.

You get a queue from the system by asking for

a certain quality of service.

And the four qualities of service,

there's user interactive,

which is I'm going to do something off the main queue,

but the user really wants this done fast, and

it's not going to take very long.

So do it right now.

Okay? So it's a high

priority queue for things that go really fast.

Then there's USER_INITIATED which is the user just

asked for this, but I know it's going to take a long time

fetching an image off the network,

we would USER_INITIATED, but it's still pretty important.

Then there's utility, which is I'm just doing something.

The user didn't just ask me to do this.

But it's just something I

need to do in the background processing things or whatever.

But it is something that matters right now to the user.

So it would be nice if it returned somewhat soon, but

it's not like got to have it.

And then there's background which is like things that

are not concerned with what the user is doing right now.

Like oh I'm pre-fetching some data, updating some database,

cleaning up a database, doing stuff in the background.

It really has nothing to do with the user doing now,

you understand what I mean?

Just background task.

So you create a quality of service,

you do that with this really odd int.

One of these constants above there,

.value, close parentheses, okay.

For historical reasons, they have them put a nice,

new UI in there Swift, which is interesting.

But anyway, you do that.

And then you just say,

queue equals dispatch_get_global_queue with

that quality of service, comma 0.

That comma 0 is reserved for future use, okay?

And that's it.

Now you have another queue.

You can now dispatch async to it, and

be running code off the main queue.

Simple as that.

You can create your own serial queues too.

I'm not going to talk about that really, but

you might have something where these queues that I'm

talking about at the top,

these quality service based ones, are concurrent.

So actually a single queue, like the user interactive

queue, could actually have multiple threads running,

working on it at the same time.

If you have a multi-core processor they can

even actually be working on them.

But mostly it's time sharing,

time slicing between the various threads.

But, anyway, you can do a serial 1.

And you would do that only if you wanted to do something off

the main thread that something depends on the next things,

depends on the next things.

So you want to want to do them serially or

you want to throttle.

Like you're fetching a bunch of images, but

you don't want to bombard, you know,

Flicker or whoever you're requesting the images from

with hundreds of questions.

You're just going to do them one at a time.

And they're going to appear in the UI one at a time.

So you can do your own

serial queues with dispatch_queue_create.

Okay?

You can also use the multi-threading stuff to

do something in the future.

Okay? You do that with

this dispatch after.

It's kind of odd again.

The arguments are kind of weird, you do this nth 64

delay in seconds times double insect per min insect per

milliseconds dispatch time now delay, blah blah.

You create this basically time dispatched_time t

that is in the future and then you just say

dispatch after that amount of time on to the main queue.

So you're almost always doing dispatch after on

the main queue.

It's rare that you'd be on another queue

because those other queues are kind of

concurrent running queues and such.

Anyway it's more likely, I want to put up an alert in

ten seconds if the user hasn't done something, or

something like that.

Which is kind of a weird UI, but anyway.

You never want to block the main queue saying oh wait,

20 seconds and then it will be able to do this.

But you want to be able to notify it.

And there's tons and

tons of stuff that I'm not even going to talk about, but

you can do readers and writers.

You can protect critical sections of

your code doing this.

You can do synchronous dispatch.

Okay, instead of asynchronously firing these

things off to put on the queues.

You can wait for them to execute.

All that stuff.

You won't need any of that for this class probably.

The things I showed you, that's the basics of it.

That's probably all you're going to need.

'Kay?

There are APIs in iOS that are multi-threaded.

So, you've got to be careful.

If you're doing something, and

it seems like this might be multi-threaded,

this seems like it might take a little while,

like it's gonna access the network or

something like that.

Then you want to read the documentation and

see, are you giving it a block, for

example, that's going to be executed off the main thread?

Because in that block, if you want to do something with

the UI you have to post back to the main thread.

Okay? So here's what

that looks like for example.

Here is an API, this is the NSURL session API.

The thing in yellow is all that really matters.

It lets you download a file from a URL.

And it does it asynchronously.

And you give it a block, which is the completion handler.

So when it's done, it invokes your block,

and it's got some arguments there.

It moves the file to a local URL that you can open.

You get HTTP response, you can get an error, et cetera.

So, you want to do something in there to update the UI,

cause you got the file back, and can you?

No, you can't, because that's not on the main queue,

however you can put dispatch async in there.

Okay, so this is exactly the same code,

I just add dispatch async in there.

All right?

So that's example where you dispatch a.

That is the exactly the same thing as I showed you on that

very first slide, it's just that it's wrapped inside an

iOS API instead of just your own dispatch to another queue.

Okay.

All right so.

Again, a demo's worth of vowels and words.

So let's go look what it looks to make an Imaginarium there.

Or Christini I guess I called it this time.

Let's make this thing be multi-threaded.

So we know that it's really bad UI, it blocks every

time we ask for these big images, it's completely stuck.

So where we fetch the image right here,

we need to be doing that on a different thread.

All right? So how are we

going to do that?

We'll we're going to do exactly what I

did in the slides there.

Which is I'm going to create one of these quality of

service things here, okay.

First of all, I don't need to do

anything if my image URL is nil.

So if it's not nil though,

then I'm going to let my quality of service equal,

let's go ahead and do this so you can see lots of code here.

All right,

I'm gonna select this quality of service to this weird thing

int and the quality of service I want is going to be this

user initiated because the user has asked for something.

It's not gonna be quick,

but I want it to be done as soon as it can.

Because the user's waiting for it basically.

So that's gonna be my quality of service there.

And then I'm just going to say dispatch, async.

The queue I want is a global queue, so

I'm going to say get global queue.

I'm gonna actually use a different one of these,

hold on.

Identifier global queue.

And use this one,

which is the one that takes the quality of service there.

So quality of service.

Not caps, Q O S, there we go.

The flags are always zero there, and here's my block.

All right?

So let's double-click to do that.

I'm going to put that outside, you know,

the trailing closure syntax here I'm going to use.

Now inside here, now I can do the things that slow.

So, let's put all of this inside here, to start.

Even though we know that's not going to be good.

All right?

So, I dispatched async in here.

Notice that it's already complaining that you gotta put

self in here.

That's because I put this inside of a closure.

And it's capturing self because I'm accessing my

image bar here.

All right, so I can fix that one.

Let's go ahead and do that here as well.

Extra parenthesis.

Okay, so that's good.

Now, this is fine,

this is that thing that's going to be slow.

It's happening on another thread right now,

that's great.

But this is not great because here, this is going to

cause UI stuff to happen all over the place, right?

Look at all this UI stuff it's going to do.

So I cannot do this on this thread.

So I just have to dispatch it back to the main thread.

The main two actually, and

again I'm going to use the trailing closure syntax there.

That's exactly why they designed it that way.

All right, so

now I'm dispatching back to the main queue.

That's good and all is well.

And that's it.

That's all it took.

So you can see the multi-threaded, it's like,

after you've made it, except for this weird end thing.

It made about the minimal amount of codes you could

possibly be.

All right, this is not quite right,

we're not quite done here, but

let's go see if this has fixed our problem at least.

So here we are, so I'm going to go Earth.

Now look, I can go back.

My UI is completely active.

I can do anything I want,

even though it's still loading that thing in the background.

And eventually, once it's kind of sucked it all down,

it shows it to me, and I can zoom in on it.

Here we go.

Okay, so that's exactly what we want.

And it took virtually no code on our part.

However, it's not quite right.

And if you have experienced multi-threading, you're gonna

immediately understand what I'm talking about here.

This is kind of lulls you into thinking,

oh I got this right okay ready to go next problem.

But what happens if this image data takes a long time to

come back and in the mean time the user asks for

a different image.

Well, we argue, I can't do that of course because we

segue to and from so we are always creating new nvc but

imagine a new nvc that stayed on screen.

And there was a way to click a button and

it would load a new image.

Well, this thing might come back after the next request

and show the user the old image they wanted instead of

the new one they'd asked for.

Do you see the problem?

So, this is where modern

threading gets a little hard on your brain.

It's like, oh, what?

These things are happening in a different order

because they're happening in different threads,

different threads of execution here.

So how do we fix that?

It's actually fairly simple to fix.

When this image data comes back, okay,

we're just gonna check and see if the URL that caused it

to be fetched is the one we currently want.

So I'm just going to go here and say, if the url, which is

this url right here, which was captured by this, okay?

So if this url up here equals self.imageURL, in other words,

the url that my user currently wants,.

Then I'm going to do this.

Otherwise I'm going to

completely ignore this response.

Okay, this JPG file came back but the user has moved on.

They don't even care about this.

So you see what I'm talking about, where you've got to

think a little bit with this multithreader.

You can't just throw the code in there and

go, oh yeah, it works.

You have to think about things might be happening,

taking a long time, cuz they're happening separately.

So that's good.

There's really nothing to show there because our UI doesn't

really allow us to specify a different image.

When we click back and forth in our UI,

we're creating a new MVC every time because we're segue.

So the new MVC is obviously got its own environment.

So the last thing I want to show you is our UI is not

very good because it has a lot of blank white screen in it.

Okay, right here, blank white screen, it's like oh,

blank white screen, what's going on?

Mm, I don't know, what's happening here?

I don't know.

You see what I mean, it's like

I can't even tell what's really going on in my UI.

It'd be nice if when I clicked here I

got like a spinning wheel that would say I'm working on it.

I'm trying to get that thing, okay.

So we want a nice little spinning wheel in here.

So how do we do the spinning wheel?

Spinning wheel, very easy to do.

We're going to go to our story board right here.

Screen realty management is a challenge on

this small screen.

And I'm just going to drag out a little spinning wheel

into my UI.

And this is a great chance for me to

show you a little bit of how to use the document outline.

And you'll see why.

So here the spinning wheel is called the activity indicator.

So I'm going to type activity, there it is.

Little spinning wheel, drag it out, and going to put it here.

Now when I drag that out, it kind of looks like okay good,

works, got a spinning wheel on there, I'm ready to go.

But actually,

something very bad happened when I dragged that out.

And if I look in the document line, I'll see what it is.

Which is that this spinning wheel

was added as a SubView of the ScrollView.

Because I dragged it out on top of the ScrollView, so

it added it as a SubView so

that mean it's going to be in that content area.

We don't want that, we want this thing to be floating on

top of the scroll view.

It's very hard to fix it in here.

Cuz it's like where do you drag it?

The scroll view's everywhere.

You can't get it.

So this is what the document outline is great for.

Watch this.

I'll take this right out of the scroll view.

Boom, it's out.

Now this puts it behind the scroll view,

because I told you the subview's list,

the top things are in the back.

So I'll just put the scroll view on top.

So now I have the scroll view in the back,

indicator in the front.

Okay?

What about constraints?

Oh, the constraints here are hard to put too,

because every time you kind of drag,

it keeps dragging it to the scroll view.

Well, I can do constraints in here as well.

Let's drag the indicator view up to

the view above the scroll view.

Right, our NVC's view.

And I'm just going to say I want this to be centered

horizontally and vertically in that view.

Boom, I've set the constraints now for

that totally using the document outline.

Now lets do one other thing with the indicator.

I'm going to inspect it over here and

you can see the indicator has some different styles.

We like grey because we

have a white background so that's good.

But it also has a couple buttons here and

this is a good one to click, hides when stopped.

That makes it so

when the spinner stops spinning it hides itself.

So it's only showing while it's spinning,

which is kind of a nice feature.

You can cause it to show and

hide yourself, but it's nice to have it hide itself.

All right, so we have this nice little spinner here, so

let's go ahead and hook it up into our UI.

And I'm just gonna Ctrl+Drag, even control dragging from

this can be a bit of a pain, because it's very small and

you got all these constraints in the way.

So you can actually Ctrl+Drag from over here.

All right so I'll Ctrl+Drag, I'll put it right here.

I'm gonna call it spinner.

I like to call my activity indicators spinners but

you could call it activity indicator too.

So here's my spinner and

what do I need to do with this spinner?

Well, I just need to

start it spinning when I make a request, and then I

need to stop it from spinning when the request is fulfilled.

Okay?

So I'm going to do that.

And I want to get lots of room here.

So I've got my spinner, well, where do I make a request?

Let's see, it's right here.

Right. This is where I dispatch

request so I'm just gonna say spinner, start animating.

But I'm gonna make that even better,

I'm gonna say question mark,

in case we're fetching an image before our

outlets are loaded.

For some reason, we don't currently do that cause we

only do it in view will appear.

But you know some day someone might write that code.

And then I could stop the spinner like down in here but

actually a great place to stop the spinner is as soon

as an image is set.

Anytime an image gets set I don't want to be spinning,

by definition.

Right?

So I'm just going to say spinner.stop animating.

Okay?

And that's it.

That's all it takes to the do the spinner.

So let's go take a look at the spinner spinning.

All right so the spinner didn't spin at

start because I haven't made any requests.

But here if I make a request there's the spinner spinning,

and yeah you can make the spinner larger if you want but

it's spinning, it's spinning cuz we're still working on

it and once the thing appears now my spinner's gone.

It's kinda hard to tell in the dark spots but here in

the light spots you can see that our spinner is gone.

'Kay same thing we come back, click on this one,

spinner comes back, right?

And I can't scroll it.

There's no scrolling or anything like that.

And then once our thing appears right here,

our spinner is gone.

Okay?

So that's it for today.

Questions?

What about the self referencing and.

Yeah so let's look at the self referencing thing.

Did we have a problem with

self referencing inside this closure.

That we did right here, and the answer is no,

because nowhere inside this closure do we refer to

anything that points to this closure.

Cause this closure just got dispatched a synced,

right it got put on some queue somewhere, but

we don't point to it anymore.

The queue points to it basically,

the queue owns this closure, we self don't.

So we're okay, we don't have to do any unknown.

But you'll start seeing the unknown thing when we

start doing animation.

When we start having actions in the animation that

are calling methods inside of our class to do animation, so

you'll see that there.

Okay? That's it, thank you.

For more, please visit us at Stanford.edu.

The Description of Stanford - Developing iOS 8 Apps with Swift - 9. Scroll View and Multithreading