A gesture-recognizer object—or, simply, a gesture recognizer—decouples the logic for recognizing a sequence of touches (or other input) and acting on that recognition. When one of these objects recognizes a common gesture or, in some cases, a change in the gesture, it sends an action message to each designated target object.
The UIGestureRecognizer class as an abstract one, it can’t be used directly. There are specific subclasses of it that are provided for usage, and each one of them deals with a specific kind of gesture. Let’s go through them for a while and let’s see what the respective gestures are for:
UITapGestureRecognizer: This class regards the tap gestures made on a view. It can be used to handle single or multiple taps, either with one or more fingers. Tapping is one of the most usual gestures that users make.
UISwipeGestureRecognizer: Another important gesture is the swipe, and this class exists just for it. Swiping happens when dragging a finger towards a direction (right, left, top and down). A characteristic example of the swipe gesture exists on the Photos app, where we use our fingers to slide from one photo to another.
UIPanGestureRecognizer: The pan gesture is actually a drag gesture. It’s used when it’s needed to drag views from one point to another.
UIPinchGestureRecognizer: When you view photos on the Photos app and you use your two fingers to zoom in or out to a photo, then you perform a pinch gesture. As you understand, pinching requires two fingers. An object of this class is usually handy to change the transform of a view, and more specifically its scale. Using pinch gestures for example, you can implement zoom in and out to photos on your own apps.
UIRotationGestureRecognizer: In accordance to the previous gesture, rotation is used to rotate a view using two fingers.
UILongPressGestureRecognizer: An object of that class monitors for long press gestures happening on a view. The pressing must last long enough in order to be detected, and the finger or fingers should not move a lot around the pressed point otherwise the gesture fails.
UIScreenEdgePanGestureRecognizer: This one is similar to the swipe gesture, but with a great difference: The finger movement should always begin from an edge of the screen.
Read Also : What’s new with iOS 13.x
A gesture recognizer has one or more target-action pairs associated with it. If there are multiple target-action pairs, they are discrete, and not cumulative. Recognition of a gesture results in the dispatch of an action message to a target for each of the associated pairs. The action methods invoked must conform to one of the following signatures:
@IBAction func myActionMethod()
@IBAction func myActionMethod(_ sender: UIGestureRecognizer)
The usual sequence of actions in gesture recognition follows a path determined by default values of the cancelsTouchesInView, delaysTouchesBegan, delaysTouchesEnded properties:
cancelsTouchesInView—If a gesture recognizer recognizes its gesture, it unbinds the remaining touches of that gesture from their view (so the window won’t deliver them). The window cancels the previously delivered touches with a (touchesCancelled(_:with:)) message. If a gesture recognizer doesn’t recognize its gesture, the view receives all touches in the multi-touch sequence.
delaysTouchesBegan—As long as a gesture recognizer, when analyzing touch events, has not failed recognition of its gesture, the window withholds delivery of touch objects in the UITouch.Phase.began phase to the attached view. If the gesture recognizer subsequently recognizes its gesture, the view doesn’t receive these touch objects. If the gesture recognizer doesn’t recognize its gesture, the window delivers these objects in an invocation of the view’s touchesBegan(_:with:) method (and possibly a follow-up touchesMoved(_:with:) invocation to inform it of the touches current location).
delaysTouchesEnded—As long as a gesture recognizer, when analyzing touch events, has not failed recognition of its gesture, the window withholds delivery of touch objects in the UITouch.Phase.ended phase to the attached view. If the gesture recognizer subsequently recognizes its gesture, the touches are cancelled (in a touchesCancelled(_:with:) message). If the gesture recognizer doesn’t recognize its gesture, the window delivers these objects in an invocation of the view’s touchesEnded(_:with:) method.
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.
The custom gesture recognizer will be looking to get either UIGestureRecognizerStateRecognized or UIGestureRecognizerStateFailed as soon as the user starts with a tap.
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.)
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 MyGestureRecognizer. Set it as a subclass to UIGestureRecognizer.
Import the gesture recognizer subclass into you new class:
Your .m file should look like this to start.
// MyGestureRecognizer.m
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface MyGestureRecognizer ()
@end
@implementation MyGestureRecognizer : 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;
}
//MyGestureRecognizer.h
#import <UIKit/UIKit.h>
@interface MyGestureRecognizer : UIGestureRecognizer
- (id)initWithTarget:(id)target action:(SEL)action;
@end
Next, write a reset method in the .m. This will reset the recognizer, getting it ready to start detecting again.
- (void)reset
{
[super reset];
self.strokePart = 0;
}
The first method touchesBegan should be set up as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
self.strokePrecision = 10.0; //A
if (touches.count > 1) {
self.state = UIGestureRecognizerStateFailed; // B
return;
}
self.firstTap = [touches.anyObject locationInView: self.view.superview]; //C
}
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.
//MyGestureRecognizer.m
@interface MyGestureRecognizer ()
@property (nonatomic)NSUInteger strokePrecision;
@property (nonatomic)NSUInteger strokePart;
@property (nonatomic)CGPoint firstTap;
@end
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event]; //A
if ((self.state == UIGestureRecognizerStateFailed) || (self.state == UIGestureRecognizerStateRecognized)) { //B
return;
}
UIView *superView = [self.view superview]; //C
CGPoint currentPoint = [touches.anyObject locationInView:superView];
CGPoint previousPoint = [touches.anyObject previousLocationInView:superView];
if ((self.strokePart == 0) && ((currentPoint.x — self.firstTap.x) > 20.00) && (currentPoint.x > previousPoint.x) &&
((currentPoint.y — self.firstTap.y) <= self.strokePrecision)) { //D
NSLog(@”Stroke part 1 complete”); //E
self.strokePart = 1; //F
} else if ((self.strokePart == 1) && (currentPoint.x < previousPoint.x) && (currentPoint.y > previousPoint.y)) { //G
NSLog(@”Stroke part 2 complete”);
self.strokePart = 2; //H
} else if ((self.strokePart == 2) && (currentPoint.x > previousPoint.x) && ((currentPoint.y — previousPoint.y) <=
self.strokePrecision)) { //I
self.strokePart = 3;
self.state = UIGestureRecognizerStateRecognized; //J
NSLog(@”Gesture Recognized!”);
}
}
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.
Read Also : How to check NULL in IOS or Android !
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self reset];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStateCancelled;
[self reset];
}
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.)
#import “ViewController.h”
#import “MyGestureRecognizer.h”
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addGestureRecognizer:[[MyGestureRecognizer alloc] initWithTarget:self action:@selector(MyMade:)]];
}
-(void)MyMade:(MyGestureRecognizer *)MyRecognizer {
NSLog(@”made by the user!!”); //This will be printed when the gesture is recognized.
}
@end
RightSlidedown.h:
#import <UIKit/UIGestureRecognizerSubclass.h> // This import is essential
@interface RightSlidedown : UIGestureRecognizer
@end
RightSlidedown.m
#import "RightSlidedown.h"
@implementation RightSlidedown
-(id)initWithTarget:(id)target action:(SEL)action{
if ((self = [super initWithTarget:target action:action])){
// so simple there's no setup
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if ([touch locationInView:self.view].x < CGRectGetMidX(self.view.bounds)) self.state = UIGestureRecognizerStateFailed;
else if ([touch locationInView:self.view].y > CGRectGetMidY(self.view.bounds)) self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if([touch locationInView:self.view].x < CGRectGetMidX(self.view.bounds)) self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if ([touch locationInView:self.view].x < CGRectGetMidX(self.view.bounds)) self.state = UIGestureRecognizerStateFailed;
else if ([touch locationInView:self.view].y < CGRectGetMidY(self.view.bounds)) self.state = UIGestureRecognizerStateFailed;
else {
// setting the state to recognized fires the target/action pair of the recognizer
self.state = UIGestureRecognizerStateRecognized;
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateCancelled;
}
-(void)reset{
// so simple there's no reset
}
@end
[self.view addGestureRecognizer:[[RightSlidedown alloc] initWithTarget:self action:@selector(rightSlide:)]];
Then a simple action method is all that’s required, like so:
-(void)rightSlide:(RightSlidedown *)rsd{
NSLog(@"right slide");
}
Happy Coding