[MUSIC] In the last lecture you learned about gesture recognizers, and gesture recognizers allow you to detect gestures, and it is an abstraction over touches. So when you touch on the device on the screen, the system gets touches and a gesture recognizer basically helps you, it's a simplification, it's an abstraction over touches that provide you with justice instead. And most of the time these are good enough like they are very useful and for most situations, you would use gesture recognizers and you should definitely use them more when you can. But in this lecture, we want to take a dive into that, that touches the UI touch system itself. And see how you can implement some, and write code that handles touches directly. So without that abstraction level. The lowest level of handling touches. Say you were building a drawing app, this would be how you get the touches to draw. All right, so before we go into any code, I want to talk about the responder chain. So what, in iOS when you touch on a view because even though here, I mean, when I'm tapping on this view right here. If you look at it from a top down level. You're really touching the top most view which in this case, the image view. And remember if we go to View Debugging. It's not debugging right now. If we run the app and go to View Debugging, we'll see those sort of layers of view underneath the ImageView. And you can see and you should know that there's a scroll view under it, which is doing the scrolling. And another view under that, which is the root view of the view controller, and then another in the window, which is sort of the overall, everything happens in a window of iOS. When you're panning here, let's pause right now, so let's continue. When you're panning here, you'll notice that the scroll view is handling your touches because the image view doesn't know how to pan itself. So it has to be the scroll view that's handling this, well it's called the scroll view. And the reason that is, is the system is actually passing the touches up a chain, and in this case of touches. The chain is basically from the top to the bottom, so we'll ask the image view first whether it can handle the touch, and since it's an image view, it'll say no. And then it goes to the scroll view, who then handles the touch, and the chain stops. So Save. There wasn't a scroll view or the scroll view had its touches disabled or its user interaction disabled. It'll keep going down the chain and ask the root view and finally the view controller. And the view controller doesn't seem like it should handle touches. It's sort of a convenience that it can. So you don't have to always subclass its, if you're going to subclass its root view to do touches you could instead handle it in the view controller. I still recommend sub classing the view to touches because the view is, like, handling touches in view is, sort of, a better practice, but having it on the view control is also very convenient. All right, so in the last lecture we implemented the double tap zoom using the tap gesture recognizer when we see of something doing somewhere with the touches directly. Stop this, we're going to just delete this. And the mixture doesn't crash when we run and we're also going to comment out this line, in viewDidLoad. So we're going to do it a little bit differently because the scroll view has already a complicated set of touch using just recognizers. It has a pretty complicated set method of handling touches. So we wouldn't want to subclass these scroll view and sort of handle touches directly as they might mess with That what I've already complicated, touch handling has. So instead we're going to subclass this image view and handle touch as an image view. This will be a little bit different than before because now it will only trigger if you touch the image view itself. Let's see how that works. We're going to add a new file. And it's going to be a subclass of the img view and we're going to call it our photo view. And we'll get a blank subclass of the image view. Has no methods, that's perfect. Create some space here. Now, and it's importing Cocoa, we don't want that, we want to import UIKit. Yours might not have that problem. I probably accidentally chose a OS10 file instead of a OS file. Okay, and then we want to change our image view to be this new subclass we have. [SOUND] And once that's done, we're going to handle touches by overriding these following four methods, touchesBegan, and you should get some autocompletion here, touchesMoved, going to hide this panel, touchesEnded. And touchesCancelled. All right. As the names suggest, these are pretty much all you need to handle any sort of touch gesture in your app, on your view. When the touch goes down, touchesBegan is triggered. And when it moves around or even sort of vertically due to pressure changing on iPhone 6S and 6S Plus with 3D touch, touchesMoved will be called. So remember that if touchesMove may be called as even as the touch is actually moving, but only the pressure is changing. And then when you lift up, you want to lift up their finger and the touch ends, touchesEnded gets called, and touchesCancelled will be called in some cases. For example, in some specific cases where it say a gesture recognizer, it'll say the pan adjuster recognizer on the scroll view. Well first let you handle the touch when it comes down, but if it starts moving enough it will recognize it as a pan gesture recognizer. Then tell you to not, it will cancel the touch on your view. Because now it's under his control. All right, so the first thing you want to do is call super. And this is a good thing to just make a habit out of, for most system events. Unless you're sure you don't want to call super, you probably want to call super. And this allows the system to do its [COUGH] normal thing. So this is ImageView, so it probably doesn't handle touches by itself, but say it was a ScrollView, and you're overriding touchesMoved. You want to let the ScrollView also do whatever it used to do. So you want to call super, which will call the original implementation on a ScrollView. And we're just going to remove touchesCancelled because it's sort of a special case and we're not going to be hitting it here. All right. So with this, we're just going to run it first. See this in action. So we're going to set a few break points to make sure they're being called. And if you touch down, guess what. Nothing happens. And that's a common thing because image view, by default, does not handle touches. And so there's a property in All Views called User Interaction Enabled. And as you can see on image view, it's off by default. If we look at a button for example, it would definitely be on. That's just a sort of convenience default for you. And if a user interaction disabled is off, you're basically out of the responder chain. It will just ignore you and as you can see as we touch, tap down. Touch begin is called, and if I continue, touch is end is called because I already let go of the touch, and touch is moved was not called because I didn't move. And to get a better idea of what's going on, we'll probably, let's log some stuff. So when touch is moved, [NOISE] let's get the position of the touch. So first it's a set but we know that the set only contains one touch because we're only touching done once here. But it can contain more than one touch. In this case, we only really want one touch. So we're just going to grab the first one, Thought first. All right there should be a first one, we're going to option on wrapping here just in case. It should never called without a touch because then why would it be caught. So if we get to touch we can get to this location by using a convenient method on UI touch which is locationInView. So this actually provides the location of this touch within the views on COINS system. And this is very important, because say for this button here its COINS system that's from its top left corner. And so if you touch on the button, and you were to handling this touch in the button, the absolute point of the touch is relative to 00 here. But by calling locationInView, it would give you the relative location to your widget, which is very, very convenient. And so here, all we're going to do is print it out. Location.x. Location.y. All right, that's all we're going to do for now. Let's run again. So, now if I start touching, and moving. Here touch is cancelled is kicking into effect because of the scroll view. You'll notice that touch is move got called a few times, but then the image started panning. And then the scroll view is like, okay, now I've got this. I'm panning now, so you can stop and so, we get cancelled. And that's okay. That was just a demo. If you remove the scroll view, you could go forever and this should never stop. It will never start panning, and so touchesMoved will keep on getting called at a pretty quick rate. As you can see, in a split second sort of a few, two or three touchesMoved gets called. All right, now to implement what we want, which is a double-tap. As you can see, this is where you can see that it's really nice to have a gesture recognizer. Because a double tap is not that easy to track, you now have to actually track this yourself manually, and keep track of the time between the two taps. So to do that we're going to have to keep track of the last touch time and we'll just keep the date of that. Is that representation of the time even though it's called a date. It's an absolute time and it's going to start with new. So we can initialize that to new. And so we're only going to care about touches, so if you have a proper touch, you'll get touches ended. If it gets canceled, you won't get it. So let's just remove this code up here. And we don't really care about the location. We're going to check if the LastTouchTime exists And if it does, we'll just compare it with the current date, so let's just do this. currentTime, and by default NSDate stores the current time. And we can check. currentTime.timeIntervalSinceDate. If this is less than, we're just going to say 0.5 seconds, then we have a double touch, like we touch twice, pretty quickly. And we're going to log something. Double Tap and once we do that we clear out. LastTouchTime and this is really bad. PreviousTime because we shadowed the variable, so it's going to be hard to set that one. And so we're going to set last this time and put in nil because I do recognize it. Else. Or, sort of there's a double else here. Else if the last touch time does not exist. We're just going to assign it to our current time. Also, if the time has passed, we're also just going to assign it to our current time because the previous one was discarded. All right, let's try that. Touch one, nothing happens. If we touch twice, quickly, we get a double tap. Pretty neat. But this is not a sort of, I can probably get this to happen even if I move quite a bit. I tap first here and tap a second time here, as long as it's within 0.5 seconds, which is actually a pretty big window. When you're talking about like, touch and taps, we can sort of cheat so. Let's clear this. But if I do it really slowly, I don't get a double tap. But if I do it like very slowly, I can do a double tap, I also get it. So this logic is pretty nice and it works first. And so there you have it. Here's how you can handle touches, using the UITouch object itself. And with this you can do all sorts of stuff, you can check the location, if you want to not allow sort of this kind of tapping around. You can save the location and check that as well. But for most cases, especially ones like these, you're going to want to use a Gesture recognizer because they're a higher level of abstraction and that's almost always provides a nicer API.