Developing iOS 11 Apps with Swift - 10. Multithreading and Autolayout

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Stanford University all right well welcome to stanford cs193p is this fall of 2017 90s the lecture number 10 and we have two very important topics today the first is multi-threading and the second is auto layout size classes so let's talk about multi-threading first multi-threading is all about for the purposes of this lecture anyway keeping long-running things off of the main queue where the UI runs and that's because we want the UI to be incredibly responsive when someone touches down will immediately respond to their touch and not be having our app freeze that would just just death to have your app freeze even for one second is an eternity for your app to just be not responding at all so multi-threading is a much bigger topic you can do a lot of other stuff with multi-threading but we're gonna focus on trying to get long-running things things that might block off on to other threads now the way that multi-threading works in iOS is using queues okay so I'm using queue in the sense of like you go to the movies and you stand in line that's a queue right and what's in the queues in iOS multi-threading is blocks of code almost always closures that you put in this queue and so you got these queues there's multiple different kinds of cues and the system then comes along and it has threads ok threads are threads of execution there are essentially opportunities to run code and it can run them kind of in parallel ok I actually guess if you had a multiprocessor or multi-core processor it could actually run things in parallel but even if you had a single core single thread of execution processor the operating system knows how to share that time up in tiny little increments between all of these threads of execution so the OS comes along and takes things off the queue the next person in line the next closure in line takes it off and it runs it ok so that's how we do queues and there's two kinds of queues we have serial queues which is iOS comes along take something off the queue and then as soon as that thing has run to completion then it goes back and gets another one off that queue that's called a serial queue then there's also concurrent queues where iOS comes along and it grabs something off the queue and it starts at running and maybe it has another thread that it can use and it goes and grabs another thing before the other ones even finish and maybe another and so it might have two or three or four ten things running off of the same queue all at the same time okay that's called a concurrent queue so we're gonna talk about both of those kinds of cues because we're gonna use both those kinds of cues to accomplish what we want and what we want is for the UI to be unblocked and the UI runs on a single serial queue called the main queue and not only does the UI run on this it's the only queue that can have blocks of code put on it that DUI things so we keep all of our UI stuff here that way we don't have to worry about multi-threaded UI where we've got two different threads of execution both trying to draw into the same space or whatever we never have to worry about that in iOS because all drawing all you activity happens on this one cue okay and since it's a serial queue it all happens on a single thread okay so you never have to worry about multi-threaded UI activity going on now the way the main queue works is it mostly sits there waiting for a touch event to happen and when the touch event happens it process it figures out what code to run runs your code and then goes back into this quiet state waiting for another touch well when it's in that quiet state it could also pull something off the main queue and run it okay so you can put things on to the main queue blocks of code and it will run in the UI thread the main queue the main thread okay so our goal is to get everything else off the main queue anything else is going to take a long time or certainly it's going to block waiting for the network or something like that we definitely want that off the main queue so where do we put those things well we put those in global queues okay now there's actually you could create your own queue to do that but we're going to use one of these for global queues and these are concurrent queues that you can throw your code on and the system will just come along and run them and there's really no restriction there about what you can put in there it's just it can't be UI code but you can do other stuff all you want so let's talk about those cues how do you get the main cue how do you get a hold of it well there is a struct or class called dispatch cue and it has a static var called main that gives you the main cue okay so now you have the main cue you're ready to go and I'm gonna talk to you soon about how you put a block of code into the cue right put it at the end of the line waiting to run the global shoot cues are a little more expressive there's not just one of them there's four different kinds here that we're going to talk about and the four of them are different based on their quality of service that's what this QoS that you see referenced here okay the quality of service tells you what kind of thing what kind of activity the blocks that you're gonna put on there are doing so let's look at the four the first one is user interactive this is a very rare one to use this means that the user is in the middle of like dragging or pinching or something and you want to do something off the main cue that has to have them so fast that it can get back to the main queue in the middle of that drag okay so we're talking about highly interactive tiny little pieces of work that you might want to throw off the main cue the reason this is unusual to use is because it's really so tiny and execute so quickly you could probably do it on the main cue okay you're probably waiting for it anyway on the main queue so user interactive much more rarely used the most common one to use is the next one user initiated so now this is something that might take a very long time or it might take a couple of seconds that maybe takes a few milliseconds you don't know but the point about it is the user has asked for it right now they touched on a button they swipe somewhere and they're asking for something to happen okay so it's initiated by the user so they expect it to be done as soon as possible okay so you are running this in the background it's not having the main thread but you're trying to do it as soon as possible so this is a very high priority queue okay yeah oh I OS is going to be pulling things off at queue and running them in threads that have higher high priority because the users asked for it right now the other two background are utility so background things are things the user hasn't in hasn't asked for right away but they're kind of things that they expect to be done fair or when you have time kind of a thing and then utility once or even lower priority those are things that your app wants to do as part of its architecture for example you have a big database and maybe every week or so or every certain amount of data in the database you want to go clean it up and remove croft out of the database that's just a utility operation so that will run at very low priority okay so you pick the global queue you want based on the quality of service you want that queue to receive all right okay so you have a queue now either the main queue or one of these background concurrent queues with a certain quality of service how do you put a block of code on to that queue put it in line to get run at some point well you do it with one of these two functions async or sync each of them take one argument that argument is a block a closure takes no arguments return no arguments there's returns no values okay so it's just basic that block is you can be and what it does is it puts that block on the queue of that you're sending it to now the difference between a sink and sink is that a sink puts it on the queue and returns immediately and then just goes on to the next line of code you have okay so now it's in the queue and someone eventually will go pull it off the queue and run it but you return immediately sync it puts it on the queue and blocks waiting for someone to take it off a queue and run it and for it to complete so you would never do sync on the main queue right because we never want to block the main queue but you might do sync on a non main queue in fact you might do sync waiting for the main queue to finish something okay when you're on another queue but mostly we're using async here because we don't really care async is short for asynchronous we don't really care when it runs we just want to run whenever it can rock so that's it so that's really all there is to multi-threading believe it or not it just leads to a little bit of interesting programming which you're gonna see here and the things you have to be careful with now I'm not going to talk about this but you can create your own queues by just calling dispatch queues initializer which has this label argument which is just that label just shows up in the debugger so you can see which queue you're on the debugger full support for queues it'll show you what thread everything is on and you can also do a lot of other things with multi-threading like protecting critical sections in your club the code or doing synchronous dispatch or locking between things you can do all that stuff I'm not gonna talk about any of that okay I'm just gonna talk about how we're gonna use that main queue and those background cues to keep things off the main queue there's a whole other API to this besides the dispatch queue API I'm going to show you operation queue and operation of the two classes involved there and those would be used if you're doing like some huge mathematical equation that has a lot of parallel processing that you could do but a lot of the parallel pieces depend on each other because operation allows you to set up dependencies okay this little block of code depends on this one running first but I'm going to start these all off and just wait until the ones that depend get finished you see what I'm saying all these dependency management I'm not gonna talk about any of that either but you get that by using the object oriented operation queue and operation api's in this class we're just gonna use dispatch q okay dispatch queue is part of what we call Grand Central Dispatch because we're dispatching these Pete blocks of code onto these queues all right so where else are you going to encounter multi-threading in addition to do your own multi-threading like you'll see in the demo that I'm going to do today you also might have iOS API that takes blocks as arguments and when it runs those blocks like when it finishes doing something organ counters an error it runs those blocks off the main queue and when you call that API in iOS you've got to really be careful that in the blocks you give to a iOS for it to run when it's done don't have any UI in there or if you do have UI dispatch it back to the main queue okay because you can only do UI stuff on the main queue so let's see what it looks like to call an iOS API like this that takes a block so here is an iOS API called URL session this is used for fetching stuff from URLs over the network we already saw in our demo on Monday how we did that with the data object but that was kind of a dumb thing you couldn't you can't get any HTTP response out of it the errors you know you have to handle them kind of funny this is much more sophisticated way to request something on the network okay you are all session as a very simple API you just create a URL session with a certain configuration you great thing about URL sessions you can do things like I want my timeout to be five seconds for example okay you can't do that with the data thing it's got some built-in timeout here you can i specify how long you want to wait before you timeout on the network or whatever so you create your session with some configuration usually the default configuration now all you need to do to make it go fetch something is create a URL and call the function on the session called data task with URL a data house with URL create what's called a data task a task to go get some data and it starts out paused and then your gonna say the very next line you almost always say resume and that starts it going okay now notice that that data task with URL function takes another argument I'm using trailing closure notation to put it outside the parentheses but it's just an argument to data tasks there and that argument is a closure that this nsurl or this URL thing is going to call URL sessions going to call when it gets the data and you can see the closure actually takes of the data that it gets as an argument which makes sense also responds in error which is HTTP response and any error that might come along in there okay so don't worry too much about that but the bottom line is you give it a closure now the interesting thing about this closure is its executed off the main queue okay so if you want to do UI things in there which you might well want to do because you want to do something with the data you got back put it in the UI can you do it in there and the answer is no you cannot because you cannot do UI stuff off the main queue okay but what if I need to do UI stuff how do I do it well you're just going to dispatch back to the main queue in there okay dispatch queue main get the main queue async put some closure on that queue and you do your UI stuff in there now this all looks almost too good to be true it's so easy right but there's a little bit to think about here in terms of timing so I'm going to walk you through the timing of this code this is exactly in code with just some labels on the lines there and we're going to go through the step-by-step how this happens first line a execute of course we're starting to do this URL session request then line B where we create this data task this line B returns immediately it immediately creates the data task and grabs that closure and hold on to it for a second and starts the task paused so that's the second line that ice cubes then line G because of the fact line be returned immediately of course line G executes next and you say resume okay that starts that task fetching the URL over the network in the background not in the main queue so the next line that executes is line H okay because we resume just instantly starts in the background but line H continues in the foreground and runs okay what do you in the next line that executes is well about five seconds or five hours or five minutes or whatever later line C will execute this happens when the data comes back from the URL okay so now I'm doing something with the data processing it looking at it we're doing whatever I want then line D executes because I need to do some UI work so I dispatch a closure that says do UI stuff here on to the main queue and it gets in line to run on the main queue but make you might be busy or there might be other things in the on the main queue and head ahead of this closure so this is not going to execute immediately instead line F will execute immediately so notice that line he has still not run okay but line F has okay what happens next finally line II run whenever the main queue is quiet and ready to do it and all the other closures that were ahead of it in line run then he runs okay so you see how what's happening with what runs win so the code makes it looks like the stuff will run top to bottom but it doesn't it runs a little out of order there because it's happening in a background thread alright so the summary is that it goes a B G H C D F E but it's this is only the most likely order because this is threads things are happening simultaneously it is possible believe it or not that line e the UI stuff could actually run before line F it's possible it dispatchers of the main queue and the main queue immediately grabbed it the main queue is a very high priority queue maybe it immediately grabs it and starts running it before F has a chance to even run if you have a single process early that would definitely be possible okay so that's the most important thing to understand about multi-threading is it's kind of asynchronous nature and that will take some getting used to believe me all right so the demo I'm going to do of multi-threading is we're gonna take Cassini and we're gonna enhance its user interface a little bit and we'll then we are going to where's Cassini I have Cassini up here yeah I do let's just hide that oops just go like this all right so here's Cassini so right now Cassini just displays this image this demo URL that we made right here so I'm going to comment that out and instead I'm gonna build some UI to use my image view controller as a generic image viewing MVC I'm going to add another MVC similar to like we did with the themes when we chose the themes this MVC is going to pick which Cassini image I want to look at I have some Cassini images which I'll show you the URLs for in a second that were taken related to the Cassini probe to Saturn and I want to look at them on screen so let's start by doing our UI first so let's go just go all UI here so here's our UI our existing kind of generic image viewing MVC and I'm gonna go down here and create a new view controller just drag it out here of course view controllers all need to have their own subclass so I will create a new subclass I'm gonna call this my Cassini view controller okay I'm gonna make sure I put it in the right place okay not at the project level all right so here it is okay I'm going to select it and make sure I go to my identity inspector over here and change its identity to be image view controller I'm gonna have it be the place I enter let's actually let's go and build our whole UI in fact let's make this work both on iPad and iPhone so I'm gonna take this guy and I'm going to embed it in a navigation controller I'm gonna take this view controller right here vedat bed in navigation controller and then I'm going to take this whole thing right here and make this be the master of a split view and this is going to be my detail so let's zoom way out here and grab a split view controller this out adds usual our split view controller brings out the document outline and has all this extra stuff here oops we don't need any of this extra stuff so let's delete that then resume back in grab our split view controller here let's make the split view controller be where we enter right let's make this be our master let's grab this and bring it down here and make it be our detail okay so we've set this up what hopefully you're used to doing this you're doing this on your homework pretty straightforward so now we've got a UI that will pretty much work on both devices both split view on iPad and then also it'll work on an iPhone so we need to prepare this image view controller right here and we need to have or sorry prepare this one and we need some UI in this one to choose the cassini image so I'm gonna do the same kind of silly thing I did before but not quite as silly as it was last time which is to put three buttons in here so I have three Cassini images so I'm gonna have three buttons let's go ahead and make a big font here 40 points in to be my favorite size font and the three images I have I have a image of Cassini itself and then I have an image of Earth and then I have an image of Saturn okay because oops not SATs run that turn get Cassini is a probe from Earth to Saturn and I'll do a little review we're gonna embed this in the stack view okay I'm gonna make it fill I'm gonna make them be the same size buttons I'm gonna set this standard spacing here let's go ahead and put this in the middle I'll go ahead and control drag to make this always be centered vertically and horizontally you can it's a little off-center from there so I'm gonna go over to my document outline which is where we resolve all auto layout issues with this button up here I'll click on that here's all my problems it's just this one problem this thing's not positioned where it should be I'm gonna click the little triangle and fix it either by updating my frames or updating my constraints in this case my constraints are right but the frame is wrong so I will fix the misplacement it moved it no more auto layout issues we have a great UI there okay it's a quick review of auto layout I'm going to try and review Auto layout as much as I can because there's a lot to Auto layout alright so now we're going to set up some segues from our master here to our detail and let me do this one a little differently before remember before I had one kind of segue one identifier and then I looked back at the button title and I said that's kind of bad code because what if those button titles were in French and then it's like ah so I'm gonna do this a little bit different way the real way I would do this probably is I'd use a table view okay which is this extensible the table list of things and then I could both add more things and also it could easily find out which one was picked by its index into the Royal tables but I haven't taught you table view yet so that's why we're using these buttons solutions alright so I'm going to create a segue from Cassini down to here it's gonna show detail because we are in a split view same thing with Earth show detail and with Saturn show detail but instead of having them all have the same identifier I'm going to give them each a different identifier which is which image to open that way if I change the language here they'll all still open the proper image so here this one is the Cassini one right so I'm gonna have its identifier just be Cassini and then this one is the earth one so we'll have its identifier be earth and this one is Saturn so we'll have a speak Saturn okay so I'm just setting the identifier of the segue so I know which one to show a little bit better more kind of accurate way to do that but now of course we need to prepare our generic image view controller set its model its public model which is that image URL so let's go look at our Cassini view controller code right we'll clean out all the junk all right these are view controller life cycle methods hopefully you recognize we'll get rid of those let's uncomment out our navigation stuff here and do our prepare for segue so how am I gonna prepare for the segue well I need to look at the identifier to see which image I'm gonna show so let's say if we can let identifier equal the segues identifier in other words it's not nil then I'm just gonna see if I can get a URL here's my demo URLs I showed you this before but let's show it again see I have these this dictionary here this national dictionary that has strings as the keys and URLs as the values and so for each one cassini I have a little URL for Earth I have a URL for Saturn I have a URL so I'm just going to look up in this dictionary to see if I can find the name of the identifier and if I can then I know I have an image to show so let's do that I'm just gonna say oops not identify identifier I here repair fire okay so here I'm gonna say if I can let the URL equal my demo URLs dot NASA sub the identifier okay so if that comes back true I was able to get an image and so now I can do a segue to an image showing view controller which is my image view controller I've built in the last lecture so I'm going to say if I can let the image view controller equal the segues destination as an image view controller then I can set the image view controllers public API let's remind ourselves what that is here's the image view controller that we worked on last time it's public API was this image URL sets the URL you want so we're gonna set that public API image URL equal to the URL I'm also going to set the title of this thing and Here I am gonna use the button title sender as UI button if it's in fact a button get its current title now is this bad this was not so bad because here on sending the title of that destination image view controller to be the same as the title of the button well the presumably the button is localized to the local language so I'm putting a button title now in the title it's not so bad it's not a bad this is not bad because we're talking about UI elements that have been localized in both cases right there okay so that's really all we need to do to prepare let's go ahead and run and see if this works we'll run this on the iPhone here and hopefully when we click on one of those buttons it will go out and hopefully the network will work today and it will fetch those image now those images are quite large so it might take a while to do right so here we go it comes up showing the detail right we didn't put the little split view trick in there to make it collapse of things so let's go back here here we go here's our UI Cassini Earth and Saturn let's try Cassini we go oh it didn't work okay so why did this not work let's go oh sorry I missed did something the storyboard let's go back and look I love it when the students are on top of it so what did I do here oh this one right here oh yeah huh that's interesting yeah okay so when I set the identity of this right here I accidentally set it to image view controller it should be a Cassini view controller where is my Cassini view controller there it a good catch right there okay probably could've looked at that for five minutes alright so yeah so I accidentally when I made it I picked the wrong one so here we go that's a common thing to do actually to either forget to set this guy's identity or to set him something wrong so why did that make this not work well because this prepare right here was not getting called because that was not a Cassini view controller but now it is so let's try it again okay so here we go go back let's try it Cassini hopefully it doesn't seem to be working oh no it crashed okay now it crashes we hate crashes but hopefully we're getting comfortable with looking why crash has happened especially looking here at our back trace so let's look and see what happened here why did this crash well we're crashing in our image view controller right there starting image view controller where we set the image okay this is where we set the image of something resize to fit and then we adjust the content area of our scroll view right but oh no something's nil here well we know the image view can't be nil because we set it right off the bat to be an image view so it must be that this scroll view is now let's see itself here's myself down here oh yeah sure enough self scroll view is nil huh well let's go back up the trace here and see what's happening okay this is happening because someone set our public API that makes sense and we're setting the image to nil to clear out any old image and then where was that call from Oh prepare for segue oh yeah okay anytime you have a crash something's nil and you were in your prepareforsegue you're gonna go oops I have an outlet and it was not set because that's what happens right prepare happens before your outlets are set and so when we look up here this is an outlet scrollviews and outlets and right here and it's not yet been set because we're preparing so what's a good fix for this how about we just optional chain okay that makes it so that this line will be ignored if scrollview with dill and that's okay because all that's happening here is that we're trying to set the image to nil anyway but since we're just preparing it's going to start out nil so it won't hurt anything to do that okay so here's what we want to do that okay it's simple fix to it so I obviously left that in there again so we could do this again because I know you guys run into this crash and you want to make sure you're comfortable with diagnosing it so let's go back let's try again now cassini oh oh it doesn't see my good work just oh wait it did work so what happened there okay so see is this my cassini image oh yeah look at that there's a Cassini probe right there I said let's try another one let's go try Earth oh no it didn't work oh well let's go to landscape Oh how come it's not going to landscape here I forget it let's go back the back button ah doesn't work what's going on okay so you see how my UI was stuck there I was trying to do back I was trying to rotate my UI was just blank not doing anything see my UI is completely and utterly stuck what I wear I touch even if I rotate nothing happened well this is a horrendous experience for your user okay this is the kind of experience that will cause your user to go and delete app okay because you cannot have your UI freezing up like that so that's why we have multi-threaded as such an important piece of the kind of app development we do in iOS so let's go in here and see if we can fix this with multi-threading so how are we going to do that we're going to go back to my image view controller this is the thing that's hanging in fact it's this line of code right here that is hanging my app right this is where I'm trying to go out on the Internet get that Cassini or earth URL and it's big and even on our fast Stamper Network it's taking a long time to come over so this is route the line of code is not returning so I cannot have this line of code executing on the main queue which is where all my code pretty much runs unless I specifically put it somewhere else or unless I use some iOS API that puts it somewhere else all right so how do I get this off the main queue well I just use this dispatch queue main queue and then use the async funk to put a closure onto the queue so this is going to take this code and run it run it on a different queue now I don't want to run this on the main queue though I want to run it on one of those quality of service queues so how do I do that I'm gonna call global queue and the argument I want to it is quality of service not cute QoS quality of service and the one I want is user initiated because the user did just ask me to do something right so I'm going to put it on the queue that gives me that good quality of service that I want from something initiated and what am I going to do here well I'm just going to do this right here I'm just gonna put this right there and go do that and this will immediately stop my app from blocking like that because it's going to be doing this Network fetch on some other queue now see it's complaining here because of course this is now inside of a closure okay so I have to do self like that now every time I do this what did I tell you to do check to see if you have a memory cycle right and we don't have one here because self does not have a pointer to this closure okay there's no pointer inside self the points of disclosure so there's no cycle however with multi-threading when you do this self dot you have to think of another thing okay which is what happens if the code before this line that has itself takes a long time to execute so long that this view controller doesn't even want to be here anymore can't you imagine that easily happening someone clicks to go get an image the image is taking five minutes to come along they're like forget it and they click somewhere else okay go back or something now that view controller that requested that image it's meaningless it has no value it should not be in the heap but it's being kept in the heap by this closure do you see because I have a reference to self in this closure this view controller my image view controller is being kept in the heap for as long as that image request is outstanding so this is a case where I want to do weak self here okay not having anything to do with memory cycles but having to do with the fact that I don't want self held in the heap by this closure if this closure takes so long to run that the user doesn't care about self anymore then I don't care about self anymore so by definition I want it to be weak everyone any questions about that so that's just another thing you need to do when you type that self dot because Swift warned you about this inside of closure if you're doing multi-threaded think about whether things want to be weak here or whether you really want to hold things in the heap okay so this is good but this is not gonna work either while this will stop my UI from blocking it'll probably screw up my UI probably cause my UI didn't draw all funny or get completely wedged and why is that because this line of code okay here I'm setting image which is this thing right here and if you look at the setting of image it sets a UI image views image that's a UI thing its size to fit that UI image view think that's a UI thing it's set to scroll views content area to a certain size that's a UI thing i'm doing all kinds of UI stuff when i set this image so i can't do this on the cue that i put this coat on okay this global queue is not a UI q i can't do it there so what i have to do here is to dispatch q main async this code back to the main queue okay now it's gonna get in line and run on the main queue when the main queue is quiet see that so this is cool this really that you can see this code is awesome this is like the easiest multi-threaded code you can possibly write but you do understand that this stuff is happening perhaps a minute after this line of code right okay because we're putting on another queue it might be blocking on the network now that leads us to another one last thing we need to do here which is what happens if we request this thing and not through our UI but some UI someone calls this image URL and sets it to something else okay they set this image URL to something else and we go to fetch that image what happens when this image comes back we don't care about it we're off on do working on a new image so when this comes back we need to check to make sure that our current image URL is the URL we requested here and we can easily do that by just saying URL equals our image URL okay this is week so so here I'm just checking after this maybe takes five minutes I'm checking to see if that URL is the one I asked or because if it's not I don't care about it anymore in this class do you see so this is what I'm talking about where when you're doing multi-threading you have to think about the timing of things things might take a while and they come back things might be different than they were when you left ok so this is a great little piece of code to really understand because it covers a lot of ground from the weak self and checking this and dispatching back to the main queue getting one of these background cues ok really make sure you understand this little piece of code right here all right so let's run and see if this has indeed fixed our nice UI to be responsive ok here we go let's try it again let's go earth and can we go back whoa look at that I can go back as an on one Cassini here comes Cassini and Earth is still probably being requested and here comes earth oh and it didn't do anything that's great it just dropped it on the ground because we don't care about earth anymore I changed my mind so here's Cassini that's great right I can rotate we can go back let's pick earth now while I'm waiting for earth I could rotate my UI is completely responsive I could go back and choose something else let's go ahead and let earth arrive earth is a big image there it is ok earth seems to be a picture of people somehow maybe we'll zoom out a little bit hello it's the earth ok so this is a picture of earth made up of a lot of little people looking up and saying hello to Cassini and that's what this one is and similarly they have a similar kind of construction with Saturn here but you can see that our UI is just dramatically improved by putting that stuff on and the other you on the other thread there all right ok but our UI is still not that great because watch what's happening right now the user has no idea they wanted the Earth image and it's not here like I guess there's no earth image you know they click back we need to give them some feedback that we are off getting their earth thing okay we need some something to let them know yeah I'm working on it ok even though you can hit back you could stay here and you can see your earth image so how are we gonna do that we're gonna do that with a little spinner okay called an activity indicator it's just a little spinning view and this is a good one to show you for another reason too which is that it can sometimes get a little crowded trying to build the UI you want especially when things go all the way the edges like this roll of you so this spinner you can search for it activity it's called UI activity indicator view see it right here I'm gonna drag this out and try to put it in the center of my image view right here now this did a very bad thing okay you can't tell it looks like it's just fine but it's bad but the way you can find out and see the badness is going to your document outline if we go to the document outline and try to find this indicator right here you see it look what happened to it it was made a subview of the scroll view okay anytime you drag a view in it gets made a sub view of the thing you drop it on and we don't want that because we don't want this scroll view scrolling thing to be in the content area of the scroll view that's what a sub view of scroll view means interface builder the content area we want it to come sit on top and luckily it's super easy to pull it out on top in the document outline it's very difficult to pull it out on top over here but here we can just lift it up and put it actually that puts it behind and here we can put it in front so now it is not a sub view of the scroll view it is in front of it sitting in front of it now we also want it to be centered but not centered in the scroll view we want to be centered in the super view of the whole you know the whole MVC so how can we do that I mean oh my gosh it's gonna be impossible control-drag how do I find the edge without hitting the scroll view it constantly wants the scroll view right there well we can control drag in here look at that okay control drag up to here and say we want it to be centered horizontally and vertically in the safe area okay we got a little displacement there no problem we'll fix that update frames boom it put it in the middle okay so control dragging inside the document outline this very good way to get it things that are hard to see now another thing we want to do is this is a very tiny little controller I want a bigger one turns out there is a bigger one this large although I don't want it white because there's gonna be white on white so I'm gonna pick a different color let's make it blue let's say okay so I got this nice spinner right here this spinner by the way you can either start it out animating or turn it on when you want which is what we're gonna do also you can have this spinner automatically hide itself when it's not spinning okay and then when it is spinning it shows itself so we want that too so let's go turn this thing on and off let's go turn the spinner on and off where do we want to turn first of all we need an outlet to it of course if we're going to talk to it so let's put that outlet right here I'm just gonna control drag normal outlet I'll call it my spinner that's what I like to call my activity indicator there so here it is and we just need to turn this thing on and turn it off well the place we need to turn it on is right before we do this really long expensive operation so I'm gonna right say here spinner start animating that's how you turn it on and where do I want to turn it off well you might think I want to turn it off in here okay but again thinking about multi-threaded really I don't want to do that because what if another image request goes out and I'm waiting on it I still want the spinner to keep spinning really I want the spinner to stop spinning whenever the image actually gets set so anytime we actually set our image to something inside here then I want to stop my animating so I'm just gonna say spinner and I'm also going to be careful here of people preparing getting me stop animating okay so this is a reliable place to stop animating once I put an image there I clearly don't want that thing spinning anymore okay so let's see what that looks like okay so we didn't request anything so there's no spinner let's go here to ask for Cassini it's spinning you see and as soon as Cassini arrives it stops mini if we zoom down you'll see that it is not there it's hard to see you in this background but it's not there okay let's just quickly make sure this is all working on an iPad let's go up here iPad and run it on there just kind of especially acute on iPad when you click this if you don't have a spinner there it's really unclear what the heck is going on here okay alright earth and while this is spinning we could change our mind and go to Cassini okay now it starts now there's a new view control and starts again and how we act you see me now one thing that's kind of a bummer here is on when we're on the iPhone remember we took the title of the button and we put it on the top and there was a title but notice on the iPad there's no title here where the where the detail is okay the detail has no title on the top it's just kind of up to the upper left-hand corners kind of ugly actually so I'd really love to put a title over here just like I have a title right here so how can I do that well it actually turns out to be quite easy to do that I'm just going to take my detail right here and I'm going to embed it in a navigation controller so I'm putting this in a navigation controller not because I'm doing any navigation down there the navigation I'm doing is all controlled by this one but just cuz I want the thing to have a title okay now let's see does that work okay let's let's run just see if that works all right here we go Cassini oh oh oh no it's not work I'm not even getting my spinner anymore that broke it it put a title up here but it broke it now why did that break our code well let's look at our code and we'll see pretty obviously why it broke it in our prepare for segue what do we do we look at the segues destination which is the detail and we check to see if it's an image view controller but it's not an image view controller now is it alright if we go back to our storyboard the destination of these segues is a navigation controller not an image controller it's a navigation controller so that line of code is failing right this line of code is image BC equals their destination is image future all not true anymore because our image our destination is a navigation controller so we can check that don't let's just go set our destination here equal to the segues destination I'm going to say if I can let navcon equal destination as a UI navigation controller then I'm going to let my destination equal that nav cons visible view controller which might be nil in which case then I'll go ahead and return the nav okay and then here I'm gonna use destination and so this is going to basically unwrap the navigation controller and get it to visible view controller this would still work if there weren't a navigation control there because then this line of code would not happen and we'd just be saying segue destination here so let's see if that works okay here we go Cassini look oh we got the title we got the spinner whoo twerking okay earth put the title spinning alright now this is very common to want to do to put your detail in a navigation controller and you have to do this little indirection a lot of times what I'll do is create a little extension to uiviewcontroller okay I call it's a little VAR I call contents which is the contents of the UI view controller which for most view controllers is just itself but if it's a navcon if we have to do this little doohickey here then it'll return its visible view controller so I'm gonna say if I can let myself be a navigation controller then I'm going to return my visible view controller or if that's a nil then okay I'll return myself and if I'm not a navcon then I'll just return myself so contents just is the view controller itself or if it's a navigation controller it's the contents to the van application controller you could also do this with tab bar control you could have an else if it's a tab bar controller then show me the view controller that is the current tab you do the same thing so here I don't need to do this anymore I can just say segue destination dot contents all right this is going to give me what I want see how this cleans up this code really nicely so you'll often want to have an extension like this when you have your detail when your detail might be wrapped in a navigation controller okay simi works perfectly there all right yes yeah right here yeah okay so by letting u RL equal imageurl we're creating a local variable called URL it's a local variable local to this function and its value is the image URL okay now this closure right here and this closure for that matter closures always capture the local variables around them when they need them and since we're referencing URL inside these closures it got captured so it holds on to this even for five minutes or however long this takes it holds it in the heap as long as this closure lives okay it holds on to it but it still stays set to what it was before if in the meantime while five minutes is happening this changes that doesn't change this because this is a local copy right it's a local variable that's a copy of this thing well okay so image URL I don't know whether URL is a value type or a reference type it wouldn't matter here okay but let's say it's a value type if it's a value type then we know this copied it okay and then when we say equals equals it means the to that URL would have to implement equatable which it certainly it does okay if this is a reference type then just just grabbing a pointer to that thing and then here we're comparing the pointers so either way it's going to make sure that if it's changed we know okay good question all right get back to our slides here okay let's talk about other very important topic today which is more about Auto layout so I'm going to do a quick review of Auto layout just make sure you understand what you understand here's kind of some of the things we've seen with Auto layout so far we know to use the dash blue lines to try to tell Xcode what we mean right putting things in the center putting things on the edges by the dashed blue lines and then we can do reset to suggested constraints remember in the lower right corner which we'll try to make constraints that make the blue lines make sense but we know it doesn't really work more than about 50% of the time so then we learned how to control drag to the edges or to other views and then set equal set the widths or aspect ratio or the edges to be the same or whatever we know that we can go to the size inspector and look at all the constraints that are constraining a certain view just by clicking on the view looking in the sizing factor and we can even edit simple things about the constraints right there in the size inspector like constant values or things like that we also have a if we can get at it in the interface where we can click on a constraint and open up the inspector and inspect detailed information about the constraint okay we also know about the pin menu in the lower right that let's just set some constraints like hooking it to the edges and things like that there's also an arrange button down there by the way I didn't show you but similar let you line up front edges and things like that and we also learned that the document outline is the awesome place to go to really look at our constraints in detail because it lists every constraint as a line item it's also where the special place do you click to resolve problems with your constraints is so the document outline is just fundamental to using auto-layout so we know all that stuff understand that mastering autolayout takes experience okay you don't just have someone tell you all these things or even show them to you and it's like oh no and I'm mad I can do Austral Auto layout now you have to have laid out a lot of things and understood the conflicts that arise and what the limitations are and all that it's a really a fantastic system autolayout very powerful but it does take some experience to master so don't get too frustrated luckily you're gonna have more assignments where you're gonna have to do Auto layout and your final project you're definitely gonna do Auto layout so you will get some of that experience it is possible to do all this Auto layout stuff from code in other words not with control dragging and all that not in interface builder but actually write code to do all of this and I'm not going to teach any of that unfortunately if you want to learn a little bit about that you can start by going to UI views documentation and search for anchor and auto layout those are the two main sets of API there's a tiny API there and then there's also just documentation how to do the auto layout system you can read through all that if you really want to get to be a master of auto layout from code okay it's not necessarily that difficult but it really truly understanding requires understanding auto layout and how it's working to do it from code alright having said all that Auto layout is sometimes just not enough ok the auto layout you know so far is just not enough ok sometimes when you rotate your device your geometry changes so dramatically there's just no way to control drag to make things happen for example in concentration we know that concentration with a lot of buttons looks great in portrait all the buttons kind of look square but then when you go to landscape the buttons are all smashed down there's barely enough room to say flips 27 at the bottom and it just really gets smashed it really would be great if when I switch to landscape if I had 20 buttons let's say it went 5 across and 4 down maybe a move that flips thing off to the side that will be much better in landscape but no amount of control dragging to the edges is going to make that happen right it just can't be done it's just you cannot constrain the views enough to make them relay themselves out like that on rotation so what's the solution to that size classes now APLA mater I think a great decision that when you rotate to landscape versus portrait instead of their reporting to you here's your new dimensions figure it out they just simplified it down to two and only two values for your horizontal and vertical size okay you are either compact or regular-sized in width and height that's it they they're going to report that to you and there's a whole system for letting you know when that changes and then you build your UI so that it looks good when it's compact vertically it looks good when it's compact horizontally it looks good when it's regular vertical and it looks good it's regular vertical and horizontal right so that simplifies the system dramatically and you're going to see that it works in the vast majority of situations so let's talk about this whole size class' thing and how it works first of all you have to understand what the device is what size classes the devices are in for example iPhones are compact in width and regular in height when they're in portrait okay when you rotate an iPhone not an iPhone plus a regular iPhone when you rotate it now it is considered compact in both directions which a little counterintuitive you would think oh well now it's regular in width but no it's considered compact in width and it's amazing how often that turns out to be exactly what you want all right and you'll see that to be the case now iPhone pluses are different iPhone pluses are still compact in width and regular in height in portrait but when you turn them to landscape now they are regular in width and compact in height okay so iPhone pluses are different than iPhone regular iPhones in that way iPads are regular width and regular height portrait landscape doesn't matter they're always regular in both which you can imagine they're big icons are huge however notice that an MVC that is in the master of a split view is compact in width even though it's on an iPad it's still compacting with regular and height okay so it's not just the device you're on that says what environment your MVC is in it might be the BC situation that it's in okay so that's why we don't look at things like am I on an iPad because then I def say am i an iPad and am I in the split view okay we just ask am i compact or am i regular in my width and height and then we react to that okay so here are all the devices in a little grid for that and let me show you what an app so here's an app like a calculator similar to our concentration calculator has a lot of buttons and maybe it'll look better with five across and four down in the two cases where it's compact height see whether it's compact width or regular width it still looks better to have only four high in a compact height and when it's regular height it always looks better to have five high maybe even more it would be better but it always looks better to have five than four so this is what we're gonna do in the demo we're gonna do concentration same kind of way as this calculator okay now we have we know the size class we're in we always know that as where our MVC is doing its business okay what can we do based on that knowledge well one thing we can do is vary a lot of view properties like the fonts on a UI label for example the background color even whether a view is hidden or not even whether a view is even in the view hierarchy can be controlled by size class so if your compact height maybe you have a view that just completely hidden in compact height maybe it only appears in regular height that's perfectly controllable using your size class but more importantly and the most powerful thing you can do is have your constraints be tied to your size class okay because remember what puts anything on-screen in any place well it's just the constraints right the constraints are what constrains a view to being in a certain space in Auto layout well if you can control those constraints using the size class then you can make those views move around to different spots depending on your size class like go five by four instead of going four by five we're moving the little flipped label off to the side okay so constraint being able to control constraints with your size class is really the power of this side cause stuff now the cool thing by the way is interface builder has support for doing all of this graphically so you don't even have to do this in your code you can do it all graphically now it is possible to find out what your size class is in code for example if you want to know your horizontal size class whether your compact compact or regular horizontally you just call this method you access this object that's in your view controller called traitcollection it's the collection of all your straight traits and one of your traits is your horizontal size class you can get it back into an enum it's either compact or regular okay I suppose it could be unspecified but that wouldn't actually be that while you're running okay again it's rare to do this we're gonna do this all in interface builder and so that's something that's all visual and so I'm gonna have to show it to you and a demo this is one place where a demo is worth a billion words okay to try and explain it without the demo be almost impossible and so I'm not gonna get back to the slide so let me just summarize here that we do have a Friday section please come to it Friday it's about instruments which is a performance analysis tool and especially when you're doing the multi-threaded stuff you want to know what's taking a long time to execute right so that you can put it off into another thread sometimes it's obvious when to network blocking thing but other times you have stuff that's conditional computing that actually turns out to be taking a lot of time so using instruments to find out what that is will save you a lot of putting stuff on other threads it doesn't need to be ok only the stuff that needs to be you all know Donald Knuth right don't prematurely Austin eyes don't obfuscate your code to make it run faster unless you're sure that's actually what's running slow well you need instruments to know that ok next week we'll dive in some more topics table of you collect em you drag and drop and stuff like that ok all right so let's go and do our concentration so here is concentration it's exactly where we left off except for two changes one change I made is I turned off all the view controller life cycle logging because if we need the connect console I want to be clear so you can see things I don't think we'll need the console but yeah you never know and then I took and I added more buttons this used to have 12 so I just added a couple more rows of buttons here so there's 20 so let's see it looks like now with those two very minor changes I haven't changed into the code or anything like that alright so here we go so let's do sports look working like a charm okay looks good and of course in oh that's not so good okay I added those more buttons but it caused the flip count you know the flip count that's at the bottom here flips eight it got pushed completely off okay because and look at the buttons they're barely big enough to hold the emoji anymore also they're ugly buttons they're really flat okay a lot of blank space on the sides okay this UI looks terrible okay so we're gonna fix this UI by doing two things one we're gonna do five across and four down only in landscape because back here we like these nice square buttons here and we wouldn't want to switch to five across and four here and then we start looking bad here okay so that's one thing we do and the other thing is we're gonna move this flips thing okay in landscape to be over to the side here over on the side this will help the buttons be even more square and use the space more efficiently on the side okay so those are the two things we're going to do alright so how do we do this how are you gonna do this well we need a strategy for doing all this and my strategy is going and remember we didn't really haven't looked at this a lot but remember you can look at all your devices down here including whether they're in landscape or portrait so here I'm looking this thing in landscape and here's where my problem is it's four across and five down I want it to be five across and four down so my strategy is going to be I'm actually gonna add four buttons here and then when I'm in landscape I'm gonna hide these four buttons and when I'm back in portrait I'll hide the new buttons I added so I'm actually gonna have 24 buttons but four of them will be hidden in one or the other see that strategy so this is only this is one strategy for doing it simple strategy so we're gonna start with a simple strategy all right so let's do that now to do that so you can see what I'm doing a little more I'm actually going to see this little constraint right here this is the constraint that's holding this to this right edge I'm going to delete this constraint just temporarily here so make some space for me to work so I'm just going to delete that and it's not pinned to that edge so it moved over now I'm going to add these four buttons remember I said I'm going to put four buttons on the side so I'm just going to copy and paste one of the existing buttons in fact I'll paste it four times I'm going to select all of them I'm gonna go down here to our embed okay and make a stack view out of it I'm gonna go inspect that stack view give it some spacing I don't want it to be leading I want to be fill alright and I want to fill equally on all the buttons to be the same height alright let's go ahead and make sure that we wire it up to our card buttons array so let's do that control guy there's one and there's two and there's three move this up a little bit sorry and there's four okay so we've got in there card buttons now I want this these four to join the party okay I want them to join in here so I'm going to select both of these stack views and I'm going to embed them in a stack view okay we'll go ahead and put some spacing in there so we can see a little better here I don't want them aligned on the bottom I want them to fill however I do not want fill equally do you see why I don't want fill equally that would make this one stack view the same width as this whole big stack view so that wouldn't be really wide and those would be small but I do want this stack view to be the same width as all of these so how am I gonna do that okay well I'm going to control drag from this stack view to one of these buttons over here and say please make them equal widths and that's perfectly legal to do that to have one view and just say please always be constrained these are all constraints constrain yourself to be the same width as this one over here today and that's going to ensure that even as this gets wider that that row will always be the same width as all the other rows in here okay so I've created this new stack view right here this stack view has no constraints right if we look over here right there are no constraints on it because I just created it and so I really want it to zoom out to be constrained to the here what I really want is I want it to constrain to the top of here and to all the safe areas on the other three edges so I'm gonna try the pin again remember this pin over here so I'm just gonna try putting an 8-point pin and it's gonna do that to nearest neighbor I'm hoping it's gonna pick the right nearest neighbor all in all sides let's see what it does well maybe that worked it's close let's look it picked the top space to superview uh I don't want that I wanted this safe area there then trailing the safe area yeah leading to safe area bottom to the flip count oh this thing did pretty well but of course this superview thing is wrong so let's remind ourselves how we fix that we go to our document outline let's find it it's right here stock stack view top to the top of the view +8 and remember we don't want that to the super view we want a tier to the safe area okay so now we've got this set back up now the problem here is we have all 24 buttons showing right here see all 24 buttons that's no good so I don't want these four buttons showing in landscape so how do I do that I'm just gonna pick this stack view and I'm gonna make it disappear in landscape but not in landscape it's gonna disappear when I'm in a compact vertical size class okay which is only iPhones in landscape turns out okay so how do we do that well if we inspect this stack view I'm just going to the normal attributes inspector over here look at all these little plus signs you see these plus signs plus plus plus and one-niner plus these are all attributes of the selected thing that can be varied by size class okay so it's not just whether it's hidden or not which is what I want right here you see it is hidden right here that's got a plus but also things like the background color or for the stack view even the alignment the distribution you can have this change when you go to a different size classes pretty darn cool so we're gonna do a simple one which is hidden and the way you will vary is you just press the plus and when you press the plus it says oh you want a very hidden based on a size class well what size class would you like to vary for well in my case it's compact height I don't care about the width so I'm set width to any any width is fine but height compact yes I want a compact variation this gambit by the way you can vary based on the color capabilities of the device you're on we're not doing that it's not really not only out thing per se but it still can be varied and so I'm gonna hit add variation and watch what happens right over here when I hit a variation see we got a new hidden we have this hidden and another hidden this one is H C which stands for height compact and right now it's not hidden in hide compact but if I turn this on oh who see it hid that entire stack view because I'm in compact height right now and if I go back over here to my portrait mode you can see that it's not hidden it's still there see four cross five now it's still there but now I want this one to be hidden so I'm gonna click on this stack view select the whole stack view here if I can yeah no it's hard to let me zoom in a little okay selecting this whole stack view and here I'm gonna do the same thing I'm in portrait mode right here I'm gonna add another variants this is a variance for any width and regular height okay and in that case this one is hidden okay so voila look at that four wide five high and over here five wide and four high okay let's see if this works it's not quite gonna work but it's gonna be close all right so here we go let's go sports look at that five wide and four high excellent let's pick some things here like this okay now let's rotate whip okay looking good let's pick something down here okay rotate Oh whoopsie what happened to those see that they're gone I picked two that we're in this row that got hidden so it makes perfect sense when we went over they got hidden so how am I gonna fix that well this is kind of a clunky UI because we're using these outlet collection to gather all these cards but once I could do really easily is go back to my code here for the concentration viewcontroller and just everywhere that I'm doing card buttons looking at the card buttons instead of looking all 24 card buttons I'm only going to look at the 20 that are visible at any given time so I'm gonna create a little private bar here called visible card buttons it's gonna be a array of UI buttons also in fact it's even going to be implicitly unwrapped and it's going to be computed I'm just going to return my card buttons if they've been set and I'm gonna filter them by the buttons super view being visible because if the bothered and super view is visible then it's visible right so I'm going to say not dollar zero dot super view is hidden okay so these are the visible card buttons this is a little fragile to do this but we know that all our buttons are in stack view so this is okay you probably could find a much better way to calculate whether it's actually on-screen or not but anyway and I'm just going to now replace everywhere that I use card buttons with visible card button so here and here and here I'm just finding and replacing air base okay so now that's going to fix this problem I hope let's find out all right it's worked again alright let's go down here these are the ones in danger let's try what Oh No how could this not have worked okay that code was infallible well here's the problem here when I switch back and forth all those views get relayed out right layout subviews happens but nothing ever causes them to get re out and you know reset up from my model so they've all still got the buttons from what they were before so what I need to do is every time I relay out my sub views like this I need to update my view from a model well guess who comes to the rescue here it's view controller life cycle right you did layout sub views imputed layout sub is I'm just going to update my view from my model okay got sports now we have these down oops if the other around so go this way okay looks worked well good now we can get these offending ones and they're there now they're in random places because we reason this outlet collection but at least it's working okay now what about the problem of where's our face our flips number okay there's no flips there okay got pushed down below the tab bar down there how are we gonna fix that alright we're gonna fix that in a different way just to show you there are other ways to do these things what we're going to do is we're gonna move this flip thing up here now you could imagine let's create another one and then do is hidden on the two of them and yeah we could do that but that cost starts to get ridiculous can now I'm gonna have another haven't have to have another outlet to that other one what a pain I really just want to move this well what makes this be here what makes it be here is these constraints on it the fact that it's aligned to the center that it's hooked to the bottom that the top space is hooked to that stack view that's what makes this be here so if I could change those constraints I could make it be over here and as long as those constraints only happen in height compact whoo-hoo it'll switch right so that's what we're gonna do now how do we do that okay we don't have the little pluses by our constraints how the heck do we do that well we do that with this magical button right here you see this very four traits we don't even need this in fact we could probably have whoops you can probably don't even need document outline here so you can really see what's going on here alright so here I'm working and if I click this very for traits button right here it'll say okay you're gonna go into a mode where all the constraints that you're doing are only applicable to the thing you're in okay so let's do it ready very four traits it says do you want to vary by width or height or both here it's the height that matters to me and whoa look at that bar okay it got dark blue so that dark blue is warning you whoa dude you are editing constraints only in compact height and it's in fact it's only showing me compact height devices right here okay so that blue warning is a warning make sure you pay attention to that all right so how do I get flips to move well I got to take all the things that are constraining flips to be here and get rid of them so I'm just gonna go over here and delete all of these constraints and if I go over to my document outline here and I look at my constraints you'll see that they didn't actually get deleted see how they're grayed out why didn't they get deleted because I only deleted them for this size class they're still exist in the other size class okay so that's why they're only grayed out here when I'm looking in this one but it is good it freed up flips you see flips is no longer tied to anything and also by the way of course this little constraint right here I can click on it this will I beam right here that's got to go as well because that is no longer going to be constrained to the edge it's going to be constrained to the edge of this flips thing right this is going to be constrained here so let's go ahead and use control drag to put this thing centered vertically let's also use control drag to hook it up to the trailing space although I don't want it to be eleven point six six seven we'll do our standard eight right there and then let's go ahead and have this guy will use the pin menu for him and have his right edge there be to the nearest neighbor which happens to be flips and then also we'll do spin again to hook this bottom of this right here to also be eight from its nearest neighbor which is the bottom now all these constraints that I just put in are all only for height compact okay but now I'm done varying with that now if I go back you'll see look it had no effect over here okay flipped is still at the bottom and over here it's at the side so let's go and run okay so let's first of all let's go make sure that it's still working in the old way so here's flips Oh working fine it's staying there now let's go over and do it here oh this one whoa look oh it it's kind of jumping around a little bit why is that happening well because the 14 15 11 those are different width so that label is kind of changing it to width wouldn't it be a lot cooler if instead of being flipped : 14 if I did if I made the flips be a multi-line okay remember the multi-line stuff we did before and instead of having flipped : 0 what if I did flips return 0 this makes more space and also this 0 could be a hundred and it still wouldn't be jiggling around in and with well that's actually really cool unfortunately I just edited that and it's also going to be true over here and I don't want it over here so how am I gonna have something that it's going to be different in these two size classes that's driven by the code because it's the code that decides what these things are well we can do that in code as well let's just don't jump over here real quick here's where we do this flip I'm just going to say trait collection dot vertical size class equals dot compact then I'm going to do one thing or if it's regular then I'll do this old thing and the thing I'm gonna do in compact is I'm going to have carriage return instead of : and I was gonna run this and then we would see that this doesn't quite work but time we're time constrained I can hear the housing people outside the door so I'm going to tell you one other thing which is that this would not if we had gone there it wouldn't work okay when we switch back and forth it would not switch that's because when we switch back and forth we get need to get notified that our traitcollection changed and there's a view controller life cycle method which I didn't mention last time because you didn't know about traits but now I'm gonna tell you it's called trait collection did change okay and this gets called every time your traitcollection changes right every time your size class changes so here I'm just gonna update might flip count label because my flip count label if you look at its code it depends on the trait collection so of course every time you changes I need to update it all right okay so here we go google sports again and see this is working nicely over here it's not jumping around anymore because it's underneath and now let's go look here and we got our : look at that and we'll go back here okay that is it we finished before the crowds came in and trampled us and I will see you on Monday your homework remember it's due on Monday before lecture on Monday and hopefully you'll have a new homework going out on Monday or maybe next Wednesday see what happens over the weekend for more please visit us at stanford.edu
Info
Channel: Jakub Moravčík
Views: 1,233
Rating: undefined out of 5
Keywords: swift tutorial, swift, ios 11, stanford, university, developing, apps, for, cs193p, ios developement, ios tutorial, Multithreading, Autolayout, ios apps development, paul, hegardy, course, queue, apple, iphone
Id: PQBH1hCcA8w
Channel Id: undefined
Length: 77min 21sec (4641 seconds)
Published: Tue Dec 12 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.