iPhone Circle Gesture Detection
In Beginning iPhone Development, we have a chapter on gestures. While I think we covered the topic fairly well, I would have liked to have included a few more custom gestures in that chapter. By that point in writing, however, we were already way over on page count. Back then, we also just didn’t have a good idea of what other gestures would become common.
I did experiment some with detecting circles back then, but didn’t include the code in the book for two reasons. One reason is because the method is really quite long and would have taken a lot of explanation, which we didn’t really have spare pages for. The second reasons is that I have a feeling there’s a much easier and more efficient way to go about detecting a circle gesture.
But, since a Google search didn’t turn up any sample code out there to do this, I decided I’d update the code and post it.
Here’s a basic description of the algorithm I’ve used:
touchesBegan:withEvent:store the point where the user first tapped the screen.
touchesMoved:withEvent:store each additional touch point in the order that they come in.
touchesEnded:withEvent:, store the final point, then do a number of checks, doing the computationally cheap ones first so as to avoid having to do any of the more computationally expensive ones by ruling out obvious non-circles. Most of these checks are based on a variance or threshold defined in a constant:
- If the end point is too far away from the start point, it’s not a circle.
- If the drawing operation took more than two seconds, it’s not a circle. There’s a chance that it might be a circle, but if it took too long, it’s probably not an intentional gesture. You can remove this check if you don’t want it.
- If there are not a certain number of stored points, it can’t be a circle. This is to avoid false positives from lingering taps.
- Loop through the stored touches and determine the top-most, bottom-most, left-most, and right-most points, and use that to determine an approximate center and an approximate average radius.
- Loop through the stored points in order, making sure:
- Each point’s distance from the center is within a certain variance of the approximate average radius.
- That the angle formed from the start point, to the radius, to the current point flows in a natural order. The angle should continuously increase or decrease. If it doesn’t go in sequence, then it’s probably a more complex shape than a circle.
You can download the sample app to see the exact implementation. It’s very basic: it just traces the shape you draw and tells you either that it detected a circle, or tells you why it doesn’t think the shape counts as a circle.
I have not yet encapsulated this to be re-usable, but the technique is pretty self contained, requiring just three instance variables and overriding three of the touch handling methods. In the sample, the touch-handling code is in the view subclass, which made it easier to do the drawing, but the touch-handling should work in a view controller class as well.
You can download the sample project here.
As I stated earlier, there’s probably an easier, more accurate way to detect a circle, but until one comes to light, this is at least functional. If you’ve got an easier circle-gesture detection method add a comment!