TOMMY MACWILLIAM: Let's take a look at our next iOS app. In this video, we're going to be making an app that applies filters to photos, much like Instagram does. So let's get started. We'll open up Xcode. And again, we're going to create a new project. So we're going to click New Project. Again, we're going to have a single view app, so we'll just leave that selected. Click Next. For our product name, we can call it whatever we want. I'm going to call mine Fiftygram. Organization Name identifier, that's the same. Language, Swift. And we don't need to check off anything else here. I'll click Next. As usual, it'll ask me where I want to save it. And I'll click Create. All right, so last time we created our Pokédex app, we also selected single view application. So all of the generated code here is the same. So we won't need to go through that. Let's just jump right into making our app. So let's tackle the view layer first. Let's open up our storyboard. And just like before, we have an empty view controller here. And let's think about what we want in our app. So we want to display some sort of title bar. In that title bar, there'll be a button where you can pull up an image selector and select a photo from your photo list. Then it'll display that photo so you can see what it looks like. And then we'll have a few different just buttons that you can press to apply filters to that photo. So let's add each of those components. Let's do the title bar first. Just like before, we can come up to Editor. First, we want to select the scene. Then come up to Editor. Embed in navigation controller. We'll click that. And then just like before, we've got our title bar now. And let's just give our app a title. So we can click Title bar. Come over to Attributes and give it a title. So now let's put the button to select the photo actually in that navigation item. So to do that, we're going to come up here at the top right. Get that search box again. And now let's add what's called a bar button item. So this is something that you can only add to one of those toolbars. So let's drag that up, and let's put it up here on the right. You can see that it kind of highlights where it wants to go. And so now we have our bar item. So let's just give it a title of Choose Photo. And we don't really need to do anything else here. Let's just keep it at the default styling for now. And we don't need an image or anything like that. So that looks pretty good. Next, let's create a container where we can display an image. So let's come up to the top right. Let's just start typing image and see what we get. And there's this control called an ImageView. And an ImageView does exactly what it sounds like it's going to do. It's a container that can display some image data in your app. So let's go ahead and drag this over. And we can just sort of position it however we want. Let's try to drag it. Let's try to get it to something that's, you know, roughly a square. And then if we want to select that, you can come over here to the right and look at some properties here. The one that we really care about is called Aspect Fit. And you'll see here, there's a whole bunch of different content modes here. But this basically specifies what happens to your image if you give an image that's larger or smaller than the image of you that you've defined. And in our case, what we want to do is just sort of resize everything to show a scaled-down version of the same photo, but keeping that same aspect ratio. And that's what Aspect Fit does. It says let's take the photo, let's make it fit into this ImageView container that we've defined, but let's preserve the aspect ratio so it doesn't sort of stretch in a weird way. So we've got that selected. And nothing else we really need to change. And so last, let's just add a few buttons. So just like before, let's come up here to the top right, and let's just start typing button, see what we get. And that's the top result. It's just a simple button. So let's drag this down and put it underneath our ImageView. Again here, we've got a bunch of different options that you can choose to style your button. In this case, let's just make ours a little bit bigger. So let's bump up the font size. So let's-- I don't know. That looks pretty good. And I happen to know that the first filter that we're going to write is a sepia filter. So we can just double click this button and change the text to sepia. So OK. That's looking pretty good. So let's just quickly run our app, just to make sure that nothing looks crazy. All right. And now that our app is started out, that's sort of what we'd expect. There is no image that we've actually loaded into the ImageView yet. And we can click this button and nothing happens. And we can click this button up here and nothing happens. So let's tackle this app in two parts. Let's first write the code to open up a user's list of photos. Let them pick one and then display that in the ImageView. Then after that, let's write the code to apply filters to that image. So first, we want to make sure that everything is hooked up with outlets and actions. So let's first create what's called an IB action. That's going to be a function or a method that's called when you tap on that bar button item. So we don't really need anything in the viewDidLoad right now. So we can delete that. And now let's create an IB action. And we're going to call that Choose Photo. And so again, just like before, with IB outlets, we have this little circle indicated that it's not connected to anything. So let's come back to our storyboard. And now we're going to Control click and drag from that Choose Photo button over to View Controller. And here, you'll see, in this sent actions-- this is a list of IB actions that we've defined in our View Controller. So I'm just going to select Choose Photo. And now they're connected. Remember, we can verify that they're connected just by clicking the bar button item, coming over to Outlets, and seeing that, yep, when it's tapped, the default action that's going to trigger is this Choose Photo method inside of our View Controller. So now that we've created our action, let's create an outlet. And we want to connect this outlet to our ImageView because we know that we're going to want to access that ImageView somehow in order to set its contents. So we're going to say IB outlet. And we're going to call it ImageView. And the type is UI ImageView. So now it's come back to our storyboard. We're going to control drag from our View Controller to our ImageView. And when I let go, one of my choices for outlets is ImageView. So I'm going to click on that. OK. So now I've just wired up these two things. So we have an outlet that lets us access that view from our code. And we have an action, which lets the storyboard respond to some kind of event and call a method that I've defined. OK, so let's first write the code to show the users photo gallery. So to do that, we're going to use a built-in iOS class called UI Image Picker Controller. Kind of a mouthful. But it's nice because it gives us a bunch of functionality for free, sort of displaying all the user's photo, or responding to a tab, and so on. So the first thing I'm going to say is I'm going to say my UI Image Picker Controller. And I want to make sure that I can access the user's photos. So I can say is source available. And then we're going to say photo library. So this is just to make sure that we have permission to access the user's photo and that they're not somehow denied. And so after we've created that, let's create our picker. So that's going to be a UI Image Picker Controller. Notice here that there's some options here. But we don't really need to take any of them. We can just create a blank UI Image Picker Controller. And now there's a couple of properties we want to set on this picker. The first one is the delegate. And remember that a delegate is a way for one class to delegate behavior to some other class. And so in this case, we have this picker class. And a user's going to tap on an image. And we want to delegate how to respond to that to the class that I'm using right here. So we're going to say that picker.delegate is self. So the object that we want to use to respond to those events is this View Controller. But now we get this error message here that says, in order to do this, we need to make sure that my class is a Image Picker Controller delegate and a Navigation Controller delegate. So just like we did with Codable, all we have to do is add those here-- one, two. And that's going to enable us to define some methods that are called in a bit. So that's one. Then we also want to specify the photo source. Remember we just checked to see if the photo library was available. So let's say that my source type here is a photo library. OK, so now our picker is all set up, has all the options we need. So the last thing to do is to actually display it. And in order to display it, we're going to use that Navigation Controller. So remember that we've already created one because we already embedded our app into a Navigation Controller. And by doing that, it means that we can say Navigation Controller. And then there's a method on Navigation Controller called Present. And it takes a few different arguments. So the first one is the View Controller that we want to present. So in this case, we want to present the picker, whether or not we want it to be animated. Sure. And finally, another closure, or another callback, that's a function that's going to be called after this finishes. So in this case, we don't really need anything to happen after it finishes. And you notice that the type of this parameter isn't optional because it ends in question mark. So let's just say nil, because we don't need to do anything. So let's build the app. Looks like our build succeeded. And so let's try giving it a run. OK, so here's our app again. And now when I press Choose Photo, there we go. So the iOS simulator has a couple images preloaded on it. And then I happened to add one of my own. So when I tap on this image, the picker goes away. And now we can actually use that image. Nothing happened yet because we haven't defined what's going to happen after user selects an image. So let's do that now. So to do that, we want to implement one of the methods that's defined by UI Image Picker Controller Delegate. So the method that we want to use happens to be called Image Picker Controller Did Finish. So I'm just going to start typing did finish and just let the auto complete do the work here. So this is a method that's defined for us and is going to get called by iOS for us. And it takes a couple of arguments. The first one is the picker or the instance of the picker that called it. And the second one is this dictionary. So it looks like it's a dictionary from some key to anything. But this is actually going to be where the information about what the user selected is contained. So let's take a look. So we want to create a new variable called Image. And now let's take a look at this dictionary. So if you consult the documentation, you'll see that the key we want to use is this imagePickerControlle r.InfoKey.originalImage. And so this is going to give you back an image. And so what we want to do is just cast this into a UI image. So this just makes sure that the photo the user selected, we want to get that in an object called UI Image. And finally, just to make sure that this cast succeeds, let's put this inside of an if let. And then let's build. And we're good to go. OK. So what do we want to do with this image? What we want to do is display it inside of that ImageView. And that's actually pretty easy. All we have to say is ImageView.image is equal to that image that we just got back. So let's run this. So we run our app. Let's click on Choose photo. Let's pick My Favorite Photo. So nothing's happening. So it looks like I'm tapping this, but nothing's actually happening. So let's add a quick Print statement to see if my delegate method is being called. So let's say Print selected photo. Run again. Choose photo. And we'll click on this. And it looks like it is. So if you come down to the bottom here, you can see this is where my print statements are going to be. Every time I tap this, I'm getting a selected photo added. So it looks like my delegate is working and my method is hooked up correctly. But what's happening here is now that I've implemented this method, I need to manually dismiss this picker. In other words, we want this View Controller to say, hey, I'm all done with that Image Picker Controller. I've got the data I needed. So let's dismiss that View Controller. So to do that is pretty simple. We can just say, navigationController.dismiss. And this is automatically just going to take whatever View Controller is on the top of the screen and hide it. So same parameter as before. Animated-- sure, why not. Completion-- you don't really need to do anything with this. So let's now try and run it. So there is our app. Let's click Choose photo. Tap on a photo. And there we go. That's pretty good. So this time, we've dismissed the View Controller. And then just like we expected, here's our image. And we've automatically done the aspect fit correctly. So we don't end up with some crazy, weird stretching. So that's the first half of our app. We've opened up this user's photo gallery. We've allowed them to pick an image. So now we want to apply a filter to it. So let's take a look at Apple's documentation for filters. In general, the iOS documentation is really, really good. It's really thorough. It has a bunch of examples. And what we're going to be looking at is this part of the API called Core Image. And you can see there's a sort of really long guide here that you can sort of walk through a bunch of how this stuff works. So feel free to check that out. But in general, all we're going to need to do is take a look at what filters are built into iOS for us. So if we scroll down here, you can see there's sort of a whole category color effect. So there's a whole bunch of things built in here. There's different photo effects. So we're actually going to get a lot for free because iOS has provided these frameworks and libraries to do filtering for us. And we can just take a look at the documentation. We said before we wanted a sepia one. So if I just search sepia and click it, there we go. It even tells us sort of like what this is going to look like. So with this knowledge, let's try writing a filter in our app. So the first thing we need to do is, again, hook up another action. We want to make sure that when the user taps on one of those filter buttons, we can run some code. So first, let's create another action. So let's say IB action. And we'll call this one applySepia. And then let's jump back to our storyboard. Just like we did before, let's control drag from our button to our View Controller. And let's click on applySepia. OK, great. Now we have the code hooked up. So now let's write our filter. If you read through that Core Image documentation, you'll notice that the first thing it tells you to do is create what's called a context. This is basically an object that some underlying API calls are going to use under the hood in order to perform those image operations. So to do that, we can just say CIContext. And we'll just have that. So now that we've connected our actions, let's write the code to create the filter. So the first thing we want to do is create an object to represent the filter. So let's say let filter equals CIFilter. Looks like the constructor can give it a name. And let's call it CISepiaTone. And where did I get that? I got that right from the documentation here. Looks like CISepiaTone is the name of the filter. And then it looks like this filter takes a couple of parameters. And from reading the documentation, I found out that the way to set those parameters is by calling this method called filter.setvalue. So let's do that. So let's say filter.setValue. Two parameters here. The first is the value. And the second is this key that says what value am I setting. And again, I just found these keys from reading the documentation. But this first one looks like it's called Intensity. And it has a default value of 1. So let's just try giving it a 0.5. And then the key-- I can use autocomplete here-- I happen to know that it's the input intensity key. And there we go. So I've set the value of the intensity. The other thing we need to do is set the image that we want to filter. So to do that, I'm going to use the same method that I got from the documentation, setValue. And what we want to set is a CI image. And so we know that-- from down here are our types-- that we have a UI image. And we need a CI image. So these are just two different classes that Apple provides to work with images. But it's pretty easy to convert among them. So you'll see in this filter, we're going to be converting among a few different image types, but it's pretty straightforward. So the first thing we want to do is take our UI image and make a CI image. So to do that, we can just say, CI image. And then we can give that an image that is a UI image, which is our image that we loaded. And key we want to use-- again, we can just sort of use autocomplete here-- is our input image key. And again, the documentation told me that, so that's input image. And from reading around, you can find that key. So it looks like we have an error here. Let's take a look. It looks like this CI image constructor is going to return an Optional. So let's just use an exclamation point to unwrap that. Again, you can use if let and guard let if you want to be a little safer. But for now, let's just sort of skip that. So now we've configured my filter. So to actually run the filter, all I have to say is let my output equal filter.outputImage. And so this is going to be the resulting image from applying this filter. But remember, I gave in a CI image. And so the type of this output image-- you can see from the autocomplete-- is also a CI image. So the last thing I need to do to display this photo is just convert it back into UI image. And to do that, we're going to use this context object that we created earlier. So we're going to say imageView.image is equal to self-- or equal to a UI image. And from the constructors, we actually want to use this intermediate format because that's going to make sure that our image retains the right size. So this intermediate format is called a CG image, or a core graphics image. Again, just sort of a third type that Apple who tries to work with images, and they each have their own different functionalities. But don't worry too much about that. All you need to know is that to create one, you can just say this self.context. And then this Create CG image. Looks like it takes a couple of parameters. The first one is a CI image. Great. We've got one of those. And the second one is just the bounds of that image, so just how large is this image so that it can draw it correctly. And luckily, we know that as well there's a property on every CI image called Extent, and that just defines the bounds for that image. So OK, so it looks like our compiler is letting us know there's errors, probably some stuff related to optional. Let's take a look. Yep. It looks like there's a few things here that are optional. So we can add an exclamation point there. Try building. Add a couple other exclamation points, just depending on what types things need. And there we go. And again, I wouldn't recommend really doing this. I'd recommend really unwrapping these using guards and if lets. It's just a little bit quicker to use the exclamation points here. So let's run this. OK, so we have our app. Let's select a photo. And let's try pressing that sepia button. All right, it looks like we've just applied a filter to that image. But what happens if we press it again? It looks like every time we press it, that image is getting darker and darker, which isn't necessarily something we want. So what we actually have to do is make sure we save that original image that the user selected from their gallery. So let's do that. Let's create a new variable here. And we'll just call it Original. And the type of this is a UI image. And this just says we're going to use this to keep track of that original image. So now, rather than using the what's currently displayed in the ImageView, let's just use that original image. And similarly here, let's just make sure we save a copy of this as Original. But now let's be a little careful here, since what happens if the user presses the button for a filter before they've selected a photo. Right now, we know our app is going to crash because we just have this exclamation point on Original. So let's not do this. Let's make Original an optional, which it could be there in the event someone selected a photo. But it could not be, because the app just loaded. So now, inside of applySepia, let's just say that we want to make sure that this original is not nil. And if it is, just return and don't do anything. So now we can see compiler errors go away because we've added a guard. And we're good to go. So now if we run the app again, we're going to choose a photo. Select this. We're going to run the sepia filter. And now, every time we press it, the image isn't changing anymore. So OK, so that's one filter. Let's try seeing what else we can do with the API and add a few more. So first, let's jump back to our storyboard and add a couple other buttons. From browsing the documentation earlier, I found a couple other filters I thought were cool. One was called Noir, and one was called Vintage. So let's just copy this button. We first want to change the text to noir. And we also want to remove that IB action we added before. If you come over here to the connections inspector, you can see that because I copy pasted, that same action is actually still there. So to remove it, we can just click the little x. And now there's no action associated with that button. So let's do the same thing for our third filter. So this one is called Vintage. Just double check to make sure there's no actions associated there, and there aren't. So that's our view. Pretty simple. So let's jump back into our View Controller and create a couple of actions, just like we did before. We'll call one applyNoir. And we'll call the other one applyVintage. Now that we've got the actions defined, let's wire them up, just like we did before. We're going to control click from the button to the scene. Select applyNoir for the second and applyVintage for the third. And so now we've wired everything up. So the last thing to do is just to write the code for applying these filters. It's going to look pretty similar to the code that we just wrote. So maybe let's start by copying and pasting, but then factor out some common code into a helper method after that. So first, let's copy this. And the first thing we want to do is change the filter. So this is using a sepia filter. But let's jump back to the documentation and see what we should use instead. Looks like there's this filter Noir here. If I click it, you can see this is the name that we want to use. So let's paste that in first. And then from the documentation, it looks like there's only one parameter here. So there's no mention of intensity anywhere. So let's remove that intensity, since it doesn't really make sense here. Now, everything else here is pretty much the same. We want to set the image. We want to get the output and then convert that output into something that our ImageView can display. So now let's just factor out the end of this into its own helper method. So let's call that method Display. And it's going to take one parameter, and that's just the filter to use to display. So let's say filter, which a CI filter. It's not going to return anything because it's just going to change the image in the ImageView. And now let's take these last three lines here and put them here, or rather put them here. So the first thing you'll notice is that filter here isn't an optional. It's just a regular CI filter. So we can remove the question mark there. And now we can just call this display method. So we'll call display on the filter that we just created. Similarly, we can use it up here. So we can say display on this filter. We can delete those lines of code that were exactly the same. Make sure we're unwrapping the optional. And we'll build. Looks like we got an error. So let's see what that is. Yep, same as before. We just want to make sure that we're unwrapping things in a way that we expect. Looks like that builds. And so last, let's use the same code and apply our Vintage filter. So let's jump back to the documentation and scroll down a bit and see this one looks pretty good. It's called CI Photo Effect Process. So let's just change this string, so that, build. And so you can see here that when I'm making sure that this optional actually exists, I'm not actually using this value at all. So I can actually change this card to be a little simpler, and I can just say if optional is nil. And that's going to have the same effect. Because really what we care about is just making sure that optional isn't nil before we attempt to apply filters to it. So we can do that here as well. All right, so let's build our app-- or sorry, not optional-- original. So we'll fix those. There we go. So now we compile. And so now let's try running our app. OK, so let's first choose a photo. Same photo as before. Make sure our sepia filter still works. And it does. And now we can try out these other ones. So that's our Noir filter. That's our Vintage filter. And we can apply these in any order you want. And just like Instagram, you get some cool effects on your photos. And so that's everything for our Instagram app. In our last video, we'll be taking a look at one other app and looking at how to save data on the device.