Make a Custom UIGestureRecognizer in Objective-C

Before learning how to make a custom UIGestureRecognizer there are a few key things to know about the UIGestureRecognizer class and gestures in general. (Skip ahead to ‘Make a Custom Gesture’ if you are familiar with how gestures work).

Basics of Gesture Recognizers

A gesture recognizer/instance of UIGestureRecognizer observes and monitors touches and movements from the users finger on the screen. It does this in order to detect (or fail to detect) a gesture pattern tied to a particular view.

A gesture can be considered any type of interaction a users finger has with the screen: a tap, touch, swipe or any combination of these.

Here is the list of built-in gesture recognizers that Apple offers and that you, most likely, use everyday in all kinds of apps.

UITapGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UISwipeGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer, UILongPressGestureRecognizer

These are all subclasses of UIGestureRecognizer. We will also be subclassing from UIGestureRecognizer to make our own gesture recognizer in this tutorial.

UIGestureRecognizers and Subviews

A UIGestureRecognizer object can be tied to a view and its subviews. It is good to keep in mind that subviews will inherit the gesture recognizers attached their parent view and respond to touches the same way as the view.

Another important thing to note is that views can have multiple gesture recognizers as long as they are not the same type of gesture and as long it makes sense for the purpose of the view.

The State Machine

The UIGestureRecognizer State Machine is a concept that Apple uses to define the lifecycle of a touch gesture made by a user. And unlike the machine above is actually very simple.

As the user touches the screen and the touch even methods are called, the state will be altered at key moments. The state machine defines all of the possible states of the touch/gesture at every moment in time.

The image below (provided in Apple’s documentation) shows the states that Apple considers make up a gesture.

Apple uses two categories, continuous and discreet, to help the recognizer understand a gesture.

Continuous gestures, like Pan and Pinch, send updates about velocity and location throughout the duration of the gesture and can affect their views mid-gesture.

First of all, both diagrams show the POSSIBLE state at the top of the tree. Possible is the state that any gesture is in before anything has happened, before a tap or touch has taken place.

The diagram on the right depicts the states that occur in a continuous gesture. After the contact with the screen has been initiated the state is BEGAN. As the user moves their finger(s) away from the spot of the initial tap the state is CHANGED. A gesture can then be RECOGNIZED, if the associated view is tied to a corresponding Gesture Recognizer. Or the the action can be CANCELED if the gesture is not complete or correct.

Discreet gestures, like Tap and Swipe, send an update at the very end once the gesture is complete. The diagram on the left depicts the possible states for discreet gestures.

Once the gesture is complete it can take one of two states: FAILED or RECOGNIZED.

These states will be used to react if a gesture has been correctly made by a user touching a view, or not.

For the Custom Gesture Recognizer we will be using the discrete gestures model: so we will only detect failure or recognized.

The Touch Events

It’s clear how the states are defined, but how do we use these states to recognize a pattern in a users touch to the screen?

In order to recognize a gesture we have to think about how the information about a users touch will be collected.

Four action methods will be called at various points in the users touch in out custom gesture or in Apple’s predefined gestures.

We will need to override these four methods and add additional functionality for the custom gesture recognizer.

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event

touchesBegan: — is called when a user touches the screen

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event

touchesMoved: — is called when user moves their finger from the original point.

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event

touchesEnded: — is called when the user lifts their finger

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event

touchesCancelled: — is called if the gesture pattern is not recognized or interrupted before completion.

These method calls will collect the data needed to recognize if a user has made out custom gesture. The states will be changed accordingly if a gesture is recognized or cancelled before completion.

Making a Custom Gesture

In this tutorial we will be making a new custom discreet gesture recognizer(the recognition will happen after the final method, touchesEnded:, has been called).

The custom gesture recognizer will be looking to get either UIGestureRecognizerStateRecognized or UIGestureRecognizerStateFailed as soon as the user starts with a tap.

This tutorial will show you the basic steps needed to make a gesture. A custom gesture can be anything you need it to be, whether a tap in a certain location on the view, a tap with multiple fingers while holding down a key on the keyboard, a particular type of swipe or endless other possibilities.

To demonstrate we will make a recognizer that will detect when the users swipe forms the letter ‘Z’.

Set up a new ‘Single View’ application project. (This tutorial assumes you are familiar with Objective C and Xcode, so I will not explain the basic setup.)

  1. Our new gesture recognizer needs to subclass from UIGestureRecognizer, adding new functionality in order to observe our custom gesture. Make a new Cocoa Touch class called zLetterGestureRecognizer. Set it as a subclass to UIGestureRecognizer.
  2. Import the gesture recognizer subclass into you new class:

Your .m file should look like this to start.

//  ZLetterGestureRecognizer.m
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface ZLetterGestureRecognizer ()
@end
@implementation ZLetterGestureRecognizer : UIGestureRecognizer
@end

3. First, we need to write implementation for the initWithTarget: action: method.

-(id)initWithTarget:(id)target action:(SEL)action
{
if ((self = [super initWithTarget:target action:action]))
{
   }
return self;
}

Our custom gesture recognizer will be acting as a delegating class to a view that will recognize it and be it’s delegate. Implementing this method will let the view controller register itself (target) and the method being called as an action resulting from the gesture recognition.

Make sure to declare this method in the .h file.

//  ZLetterGestureRecognizer.h
#import <UIKit/UIKit.h>
@interface zLetterGestureRecognizer : UIGestureRecognizer
- (id)initWithTarget:(id)target action:(SEL)action;
@end

4. Next, write a reset method in the .m. This will reset the recognizer, getting it ready to start detecting again. This will become more clear at the end.

- (void)reset
{
[super reset];
self.strokePart = 0;
}

Now, we will be overwriting the four touch event methods in order to create our new custom gesture recognizer.

As mentioned above, Apple’s gesture recognizer class UIGestureRecognizer functions by collecting information from the touches events being called while the users finger is on the screen. The information it observes are interpreted by the State Machine.

We will define the four touches event methods to set the states according to our desired gesture.

5.The first method touchesBegan should be set up as follows:

Here are some descriptions of what is happening in the code:

A. The strokePrecision property is an arbitrary number that I decided would be the tolerance for how much a line could veer in the perpendicular direction when the user is swiping in a particular direction. This can be altered for more or less precision.

B. This makes sure there is not a two or more finger touch. Multiple finger touch is something you can do though! Play around with it to make your own two or more fingered gesture.

C.The first tap location will be set here in touches began.

6. The touchesMoved:withEvent: method is where we specify what our custom gesture will be. This is where it all happens!! To do this we will set up conditionals that need to be met in order for the gesture to be recognized.

Before writing any conditionals, though, we have to think carefully and visually about what the ideal gesture will look like or what individual elements will need to occur to form the gesture. For this example we will use a continuous swipe in the shape of a letter “Z”.

The letter Z is made with three connected but unique motions: horizontal to the right, diagonal down and to the left, and horizontal to the right again.

These three components will be specified by conditionals that will all by combined using if/if else statements.

Separating the three components will be done in an extremely simple way; by incrementing a regular NSUInteger property after each part of the if statement.

This property can be called strokePart and should be put in the interface section in the .m file. We should include two other properties: strokePrecision(also used in the conditional as an arbitrary number of pixels the stroke should not veer during the gesturing) and firstTap(a CGPoint that represented the first point of contact of a users finger to the screen, in the touchesBegan method).

Your interface within the implementation should now look like this.

//  ZLetterGestureRecognizer.m
@interface zLetterGestureRecognizer ()
@property (nonatomic)NSUInteger strokePrecision;
@property (nonatomic)NSUInteger strokePart;
@property (nonatomic)CGPoint firstTap;
@end

But now on to the touchesMoved method!

Here are some descriptions of what is happening in the code:

A. First it is important to call the super method of touchesMoved. This is also the case with all three of the other touches event methods.

B.This stops the touches from being observed once the gesture has been recognized.

C.This allows us to get the points from the same coordinate system of the superview.

D.This defines the first stroke, a horizontal stroke from left to right. As you can see the conditional statements are based on different relationships between the x & y values of the first tap and current point. Since this is a delegate method it will be constantly updating as the user swipes.

You can play around with these conditionals to recognize other types of gestures or to be more specific or less specific with out gesture. For now these conditionals will work for the demonstration.

E. In the body of the first if statement it is a good idea to put an NSLog and breakpoint at first to make sure that nothing is wrong with this first set of conditionals. Once you know for sure your gesture is being recognized then you can get rid of the NSLog.

F. The only real thing that we do in the body of the if statement is to increment the strokeNumber property. This says that the first part of the gesture is done and will allow for the next if else statement to be evalutated.

G. The first if else statement conditional only check to make sure that the user is gesturing in a positive y direction (towards the bottom because remember iPhone coordinates start at 0,0 in the upper left-hand corner), the negative x direction (towards the left), and that the increment has happened.

H. Once again we increment strokePart in order to start detecting the final set of conditionals in the final if else statement.

I. These conditionals are very similar to the first set since the action is the same as the strokePart == 0 (D.). They can be adjusted a bit for more precision or to add any other kind of gesture imaginable.

J. Lastly we know that if a user has arrived in the body of the final if else statement our entire gesture has been recognized! The final step will be to assign the state to UIGestureRecognizerStateRecognized.

7. Now, include the last two methods touchesEnded (which will reset the strokePart increment to 0) and touches cancelled which will set the state to UIGestureRecognizerStateCancelled, this will happen if the gesture is not completed.

8. The code for the custom gesture recognizer is complete! Now to use it in a view, like a view controller. To do this: import the header file, add the gesture recognizer in viewDidLoad and make a selector method (that will cause something to happen when the gesture is recognized.)

9. Now test your custom gesture recognizer by making your finest “Z”!

Thanks for Reading!

Special Note:

Darklings is an example of a very cool iOS game that uses many custom gesture recognizers beautifully. Check it out!

Find the complete project code on github.com/jen2.

Helpful Resources:

(A very good video tutorial for making a custom gesture for an OSX app. It uses Swift, but it still clearly explains the concepts needed.)

“Learning iOS Development” by Maurice Sharp, Erica Sadun, & Rod Strougo. http://www.amazon.com/Learning-iOS-Development-Hands-Fundamentals/dp/0321862961