Jugs
Jugs

Reputation: 231

Detect diagonal swipe gestures in a UIView

I want to detect a two-finger diagonal swipe that starts from the bottom right of the screen to the middle. I tried adding UISwipeGestureRecognizer with direction set as "UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionLeft" but to no vail as the handler is getting invoked even if I start the swipe from the middle of the screen to the top left.

Do I need to sub-class UIGestureRecognizer or can I handle this using touchesBegan and touchesMoved ?

Upvotes: 4

Views: 5616

Answers (5)

onmyway133
onmyway133

Reputation: 48085

You can use a UIPanGestureRecognizer

- (void)viewPanned:(UIPanGestureRecognizer *)panGR
{
    CGPoint translation = [panGR translationInView:self];

    CGFloat threshold = self.bounds.size.width/2;

    // Detect
    Direction direction = DirectionUnknown;

    if (translation.x > threshold) {
        if (translation.y > threshold) {
            direction = DirectionBottomRight;
        } else if (translation.y < -threshold) {
            direction = DirectionTopRight;
        } else {
            direction = DirectionRight;
        }
    } else if (translation.x < -threshold) {
        if (translation.y > threshold) {
            direction = DirectionBottomLeft;
        } else if (translation.y < -threshold) {
            direction = DirectionTopLeft;
        } else {
            direction = DirectionLeft;
        }
    } else {
        if (translation.y > threshold) {
            direction = DirectionBottom;
        } else if (translation.y < -threshold) {
            direction = DirectionTop;
        }
    }
}

Upvotes: 1

cloosen
cloosen

Reputation: 993

the touches method of cause can do every Gesture, the Gesture is supported since ios3.2. i can give you a simple code , you maybe modify by yourself.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    isTwoFingersBegin = NO;
    isTwoFingersEnd = NO;
    UITouch *touch = [touches anyObject];
    UIView *touchView = [touch view];
    if ([touches count] == 2)
    {
        isTwoFingersBegin = YES;
    }

    touchStartPoint = [touch locationInView:self.view];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    isSwip = YES;
}
#define TOUCH_MOVE_EFFECT_DIST 30
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{   
    UITouch *touch = [touches anyObject];
    UIView *touchView = [touch view];
    touchEndPoint = [touch locationInView:self.view];
    if ([touches count] == 2)
    {
        isTwoFingersEnd = YES;
    }
    CGPoint deltaVector = CGPointMake(touchEndPoint.x - touchStartPoint.x, touchEndPoint.y - touchStartPoint.y);

    if (fabsf(deltaVector.x) > TOUCH_MOVE_EFFECT_DIST
                &&fabsf(deltaVector.y) > TOUCH_MOVE_EFFECT_DIST
                && isSwip &&isTwoFingersBegin&&isTwoFingersEnd) {
                theSwipGesture=RightUpGesture;
            }
    else if (fabsf(deltaVector.x) <- TOUCH_MOVE_EFFECT_DIST
                && fabsf(deltaVector.y) <- TOUCH_MOVE_EFFECT_DIST
                && isSwip&&isTwoFingersBegin&&isTwoFingersEnd) {
                theSwipGesture=LeftDownGesture;
            }

    isSwip = NO;
}

Upvotes: 1

Marty
Marty

Reputation: 6174

You need to subclass it to use touchesBegan and touchesMoved anyway. I'd do UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionLeft like you said, then set up two CGRects, one for acceptable starting points, one for acceptable end points. Then use the UIGestureRecognizerDelegate method gestureRecognizer:shouldReceiveTouch:, and check if the touch point is in the acceptable start rect by using CGRectContainsPoint(). Then (I think) in your UISwipeGestureRecognizer subclass, override touchesEnded:withEvent:, and check if the end touch is in the acceptable rect. If it's not, set the state to UIGestureRecognizerStateCancelled (or however you're supposed to cancel a gesture). Also make sure the view it's attached to has its multipleTouchEnabled property set. I haven't actually tried this, but something of the sort should do it. Good luck!

EDIT

Actually, if you didn't want to have to worry about specific rect values for the acceptable start/end points, and make it device independent, you could do this:

//swap in whatever percentage of the screen's width/height you want in place of ".75f"

//set the origin for acceptable start points
CGFloat startRect_x = self.view.frame.size.width * .75f;
CGFloat startRect_y = self.view.frame.size.height * .75f;

//set the size
CGFloat rectWidth = self.view.frame.size.width - startRect_x;
CGFloat rectHeight = self.view.frame.size.height - startRect_y;

//make the acceptable start point rect
CGRect startRect = CGRectMake(startRect_x, startRect_y, rectWidth, rectHeight);

//set the origin for the accepable end points
CGFloat endRect_x = self.view.center.x - rectWidth/2;
CGFloat endRect_y = self.view.center.y - rectHeight/2;

//make the acceptable end point rect
CGRect endRect = CGRectMake(endRect_x, endRect_y, rectWidth, rectHeight);

Upvotes: 1

Jugs
Jugs

Reputation: 231

Thanks Guys!

I ended up writing custom code in touchesBegan, touchesMoved and touchesEnded and ot works like charm.

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

    if ([touches count] == 2) {
        CGPoint nowPoint = [[touches anyObject] locationInView:self];
        if( nowPoint.x >= ALLOWED_X)
            swipeUp = YES;
    }

    [super touchesBegan:touches withEvent:event];
}


-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    if ([touches count] == 2 && swipeUp) {

        CGPoint nowPoint = [[touches anyObject] locationInView:self];
        CGPoint prevPoint = [[touches anyObject] previousLocationInView:self];

        if( nowPoint.x <= prevPoint.x && nowPoint.y <= prevPoint.y){
        }
        else {
            swipeUp = NO;
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    CGPoint nowPoint = [[touches anyObject] locationInView:self];

    if ([touches count] == 2 && swipeUp && nowPoint.x <= DELTA_X && nowPoint.y <= DELTA_Y) {
        NSLog(@"Invoke Method");
    }
    swipeUp = NO;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    swipeUp = NO;
}

Upvotes: 0

The Kraken
The Kraken

Reputation: 3158

One possible solution would be to map off a group of coordinates on the iPhone's screen where the swipe could potentially start, and another where it could potentially end. Then in the touchesBegan: and touchesMoved: methods, you could compare the starting and ending points of the swipe and determine whether it qualified as diagonal.

Upvotes: 0

Related Questions