Simple swipe detection in the iPhone SDK
A quick round of hallway usability testing of my iPhone app revealed that apparently, iPhone and iPod Touch users like to swipe and don’t have any idea that the UIPageControl (the bar with the little white dots that show which page of information you are on) can be tapped on to move the page number. Instead, my admittedly small sample size of 2 (thanks John and Ben) were trying to swipe all over the view to move it from one page to another. As a result, I set out to try and find information about detecting the swipe motion in the iPhone SDK.
Quick searches of the internet and documentation did not reveal any immediate solutions, so I set about on the task of trying to figure out how to do it on my own. The first thing I noticed is that you can hook into the touchesBegan event for the view, so I created this code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CGPoint pt; NSSet *allTouches = [event allTouches]; if ([allTouches count] == 1) { UITouch *touch = [[allTouches allObjects] objectAtIndex:0]; if ([touch tapCount] == 1) { pt = [touch locationInView:self.view]; touchBeganX = pt.x; touchBeganY = pt.y; } } } |
(After adding the following variable definitions at the top of my view controller implementation file, of course.)
int touchBeganX, touchBeganY; |
So now I have the position where a single touch began. The next thing I thought I would do would be to subclass the touchesEnded event, but for some reason, the tapCount of the touch that I read in touchesEnded would be zero, and that seemed a little confusing to me as to why that would be. I then turned to the touchesMoved event:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { CGPoint pt; NSSet *allTouches = [event allTouches]; if ([allTouches count] == 1) { UITouch *touch = [[allTouches allObjects] objectAtIndex:0]; if ([touch tapCount] == 1) { pt = [touch locationInView:self.view]; touchMovedX = pt.x; touchMovedY = pt.y; } } } |
Yes, this looks shockingly similar to the code for touchesBegan. By the way, don’t forget more variable definitions:
int touchMovedX, touchMovedY; |
I can now track where the touch began and the last place it moved to. I then swing back around to the touchesEnded event to do the actual swipe detection, as even though the tapCount is zero, I think I can safely determine that a single touch and drag event occurred and then test it to see if it is a swipe.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSSet *allTouches = [event allTouches]; if ([allTouches count] == 1) { int diffX = touchMovedX - touchBeganX; int diffY = touchMovedY - touchBeganY; if (diffY >= -20 && diffY <= 20) { if (diffX > 20) { NSLog(@"swipe right"); // do something here } else if (diffX < -20) { NSLog(@"swipe left"); // do something else here } } } } |
Perhaps reading swipes is so simple to do in the iPhone SDK that I missed it in the docs or online. If anyone has any better ways to do this, or a web site or blog post that explains it better, please let me know.
Somewhat related, somewhat unrelated tangent alert:
My father used to have this bag that said “I Like Swipe”, I think he said that he used to sell the product when he was a boy back in the 1940s, it was some kind of cleaning chemical. I wish he still had that bag, it was pretty retro looking and a picture of it would have fit in perfectly with this posting. I guess I will just have to settle with this:
mmm swipe, i would increase the y delta some, it’s hard to swipe in a perfectly horizontal line.
[…] probably should have watched this video before trying to do swipe detection (see this blog post) in my application, it probably would have saved me some search time and trial and error. Josh used […]
when i implement the code onto my viewcontroller m file, there is a warning that the method definition was not found for touchesBegan, touchesMoved, or touchesEnded… I can’t figure out what’s wrong and it’s not letting my view load. Any help?
(sidenote: i think it’s also important to include
– (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
The touch tracking methods are a part of the UIResponder class. My view controller header file inherits from UIViewController, which in turn inherits from UIResponder. Check to make sure that your view controller header file is included in the implementation file, and check to see what your view controller is inheriting from.
Also, I didn’t really implement the touchesCancelled method because I was not doing anything during the touch tracking that was using up any memory. In production quality code, it would probably make sense to include the method if only to reset your touch tracking information back to default. (And as an aside, I am no longer using this swipe tracking in my application, I switched to a different method of navigating around the application.)
thank you, I actually got it to work by fixing the header file.
But just out of curiosity, do you know how to check that the touchesBegan started on a certain y value so that you can have different swipe actions depending on where you start swiping?
nevermind, i just set up an if statement that said touchBeganY had to be within a certain range for each action.
I’m trying this method for implementing swipes across some images using UIimageView.
I found it really easy to setup, except that on SDK 3.2 I had to remove the “.view” from the Touch locationInView.self.view”. Keeping it kept giving me an error.
Now, while the swiping gestures are working fine, I noticed that if I swipe in one direction and then I just Tap on the next view, I end up moving to the next view. In other words, once I do one swipe a simple tap is being recognized as a swipe.
Any ideas?
I think the .view is pretty important here, is the view that you are using the code on a view controller that inherits from UIViewController? Also, you might want to put some additional logging information in the touchesEnded method shown above so that you can see what the delta X and delta Y values are showing up as.
Admittedly, I have not tried this on the latest SDK, as I do not have this code in my project any more. If you run it through the iPhone simulator using 3.1.3, does it work as expected?
BP,
Great code snippet. Only problem if you simply tap it recognizes it as a right swipe. I’ve overcame this issue by using the – (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event method.
I simply created a BOOL in my h called it didSwipe.
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//if the user does more then simply touch this is called
didSwipe = TRUE;
}
(void)detectSwipe {
if (didSwipe == FALSE) {
}
else if ((touchDownLocation.y >= (touchUpLocation.y – 50) && touchDownLocation.y (25 + touchDownLocation.x)) {
//Right Swipe
NSLog(@”right”);
}
}
else if (touchUpLocation.x < (25 + touchDownLocation.x)) {
//left swipe
NSLog(@"left");
i
}
}
}
//and we set our didSwipe bool back to false
didSwipe = FALSE;
}
The code above fixes the problems 🙂
David: Thanks for the ideas. BP
It worked for me, thanks I was having a flickering problem with other code I used from a forum
You could also use the dot product instead of comparing deltas. This would free up your axes so they can scale more readily, shifting the focus toward the actual angles. Combine this with a very simple timer and you can also check swipe rate.
pseudo code for two 2D vectors, u and v (‘u’ could be your touch delta, ‘v’ could simply be [1,0] for a right-facing unit vector):
angle = arccosine( (u1*v1 + u2*v2) / (sqrt( u1*u1 + u2*u2 ) * sqrt( v1*v1 + v2*v2 )) )
if (angle < 20*(Math.PI/180))
{
// do something here 🙂
}
Thanks.
This article saved my life from searching lots of pages by Google.
I can now program swipe detection on my iPod Touch.