I am new to iOS/objective-C and I wanted to know how to build custom gestures. In particular, if a user taps the top right of the screen and slides his/her finger down the edge
You can do it by seeing either positive or negative delta in the x and y axis of the touch. for instance, a check mark gesture (√) will be a negative delta followed by a positive delta in the y while there is always a negative delta with the x and the touch ends at a lower height than where it started. Add more fingers you add more checks.
Pseudocode:
bool firstStroke, secondStroke, motion, override;
while (touchdown){
if (yDelta < 0){firstStroke = TRUE;}
if (firstStroke && yDelta > 0){secondStroke = TRUE;}
if (xDelta < 0){motion = TRUE;}
if (xDelta > 0 || (firstStroke && secondStroke && yDelta < 0)){override = TRUE;}
}
if (firstStroke && secondStroke && motion && start.y > end.y && !override){
return TRUE;
}else{
return FALSE;
}
The while command means that while the touch is down, check for 3 things:
-If the touch has moved down
-If after the touch has moved down that it has moved up again
-If the touch is moving a right to left
The forth check is to see if the touch ever moved left to right or if after the gesture moved down after the gesture has been finished.
After the touch is finished, there is one more check to see if the gesture moved correctly, if the points started and ended in the correct places and if the gesture moved in an incorrect motion (override).
Hope that helps.
Creating a UIGestureRecognizer
subclass is a bit involved to do in a solid way. I very much recommend watching the WWDC2010 videos on the subject Session 120 - Simplifying Touch Event Handling with Gesture Recognizers
& Session 121 - Advanced Gesture Recognition
. They are thorough and well done.
But for a very simple example, based on your question, I created a very simple gesture recognizer that fires when a user touches the upper left quadrant of the attached view and slides their finger down to the lower right quadrant of the attached view and picks up their finger, without sliding to the left side of the attached view.
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
So basically the gesture recognizer gets what seems like the standard touch events. (They're not, but they act that way). As you respond to the movements you change the state
property of your gesture recognizer.
There are two basic types of recognizers, "Discrete" (think tap gesture) and "Continuous" (think pan gesture). Both types automatically start in UIGestureRecognizerStatePossible
in the beginning.
For a "Discrete" type, like this one, your goal is to get to state UIGestureRecognizerStateRecognized
or UIGestureRecognizerStateFailed
as soon as possible.
The ideal usage of this example would be to add the RightSlidedown
gesture recognizer to the main view of a new "Single View Application" in the view controller's viewDidLoad
.
[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");
}