26

I need to recognize swipes in all directions (Up/Down/Left/Right). Not simultaneously, but I need to recognize them.

I tried:

  UISwipeGestureRecognizer *Swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(SwipeRecognizer:)];
  Swipe.direction = (UISwipeGestureRecognizerDirectionLeft | 
                     UISwipeGestureRecognizerDirectionRight |
                     UISwipeGestureRecognizerDirectionDown | 
                     UISwipeGestureRecognizerDirectionUp);
  [self.view addGestureRecognizer:Swipe];
  [Swipe release];

but nothing appeared on SwipeRecognizer

Here's the code for SwipeRecognizer:

- (void) SwipeRecognizer:(UISwipeGestureRecognizer *)sender {
  if ( sender.direction == UISwipeGestureRecognizerDirectionLeft )
    NSLog(@" *** SWIPE LEFT ***");
  if ( sender.direction == UISwipeGestureRecognizerDirectionRight )
    NSLog(@" *** SWIPE RIGHT ***");
  if ( sender.direction == UISwipeGestureRecognizerDirectionDown )
    NSLog(@" *** SWIPE DOWN ***");
  if ( sender.direction == UISwipeGestureRecognizerDirectionUp )
    NSLog(@" *** SWIPE UP ***");
}

How can I do this? How can assign to my Swipe object all different directions?

pasawaya
  • 11,364
  • 7
  • 50
  • 92
elp
  • 7,607
  • 7
  • 58
  • 113

8 Answers8

21

You set the direction like this

  UISwipeGestureRecognizer *Swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(SwipeRecognizer:)];
  Swipe.direction = (UISwipeGestureRecognizerDirectionLeft | 
                     UISwipeGestureRecognizerDirectionRight |
                     UISwipeGestureRecognizerDirectionDown | 
                     UISwipeGestureRecognizerDirectionUp);

That's what the direction will be when you get the callback, so it is normal that all your tests fails. If you had

- (void) SwipeRecognizer:(UISwipeGestureRecognizer *)sender {
  if ( sender.direction | UISwipeGestureRecognizerDirectionLeft )
    NSLog(@" *** SWIPE LEFT ***");
  if ( sender.direction | UISwipeGestureRecognizerDirectionRight )
    NSLog(@" *** SWIPE RIGHT ***");
  if ( sender.direction | UISwipeGestureRecognizerDirectionDown )
    NSLog(@" *** SWIPE DOWN ***");
  if ( sender.direction | UISwipeGestureRecognizerDirectionUp )
    NSLog(@" *** SWIPE UP ***");
}

The tests would succeed (but the would all succeed so you wouldn't get any information out of them). If you want to distinguish between swipes in different directions you will need separate gesture recognizers.


EDIT

As pointed out in the comments, see this answer. Apparently even this doesn't work. You should create swipe with only one direction to make your life easier.

Community
  • 1
  • 1
jbat100
  • 16,470
  • 3
  • 40
  • 69
  • Uhm, than i need to create 4 gestures like `UISwipeGestureRecognizer *SwipeLeft; UISwipeGestureRecognizer *SwipeUp; [...]`? And assign to view like `[self.view addGestureRecognizer:SwipeLeft]; [self.view addGestureRecognizer:SwipeUp]; [...]` ??? – elp Nov 18 '11 at 11:53
  • 3
    Yes. The gesture recognizer you created will tell you a swipe happened in one of the directions but not which one. – jbat100 Nov 18 '11 at 11:55
  • hello @ibat100 by using above code all the gestures recognized at one time either if I swipe left to right or right to left. – Warewolf Nov 03 '12 at 10:05
  • 2
    The last sentence, " If you want to distinguish between swipes in different directions you will need separate gesture recognizers." Really saved me time. – antonio081014 May 22 '13 at 23:17
  • if you want to use swipe gesture you have to add 4 different gesture this Swipe.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp); is not working with iOS6 – GameLoading Nov 14 '13 at 04:28
  • @fasttrack as I pointed out in the edit... thanks for the downvote – jbat100 Nov 14 '13 at 08:20
  • 1
    I tried this, but it only worked when you combine left and right or up and down. Only left and right swipe can be detected when you combine these four directions. – Danyun Liu Mar 21 '14 at 10:10
  • @Danyun I don't think you should make any assumptions (it might change between os versions, as it is not documented), I would stick to using one recognizer per direction – jbat100 Mar 21 '14 at 13:46
  • I up voted for its usefulness, but it makes me sad to see variables/method names as upper-camel-case :( – Joel Balmer Mar 25 '14 at 09:59
  • @JoelBalmer I agree, I just copy-pasted the posters methods – jbat100 Mar 25 '14 at 13:18
  • You should cast the sender into a UISwipeGestureRecognizer in the handler, or just change (id)sender into (UISwipeGestureRecognizer *)gestureRecognizer. –  Aug 11 '14 at 15:30
  • swipe left and right work properly but up and down not recognize – bhavik Sep 22 '14 at 11:30
  • Nice answer, dat capital S doe – Adam Waite Jul 30 '15 at 09:15
4

Unfortunately you cannot use direction property for listening the recognizer; it only gives you the detected directions by the recognizer. I have used two different UISwipeGestureRecognizers for that purpose, see my answer here: https://stackoverflow.com/a/16810160/936957

Community
  • 1
  • 1
Yunus Nedim Mehel
  • 11,371
  • 4
  • 47
  • 54
4

I actually ran into this exact problem before. What I ended up doing was creating a UIView subclass, overriding touchesMoved: and doing some math to calculate the direction.

Here's the general idea:

#import "OmnidirectionalControl.h"

typedef NS_ENUM(NSInteger, direction) {
    Down = 0, DownRight = 1,
    Right = 2, UpRight = 3,
    Up = 4, UpLeft = 5,
    Left = 6, DownLeft = 7
};

@interface OmnidirectionalControl ()

@property (nonatomic) CGPoint startTouch;
@property (nonatomic) CGPoint endTouch;

@end

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.startTouch = [[touches allObjects][0] locationInView:self];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    self.lastTouch = [[[touches allObjects] lastObject] locationInView:self];
    NSLog(@"Direction: %d", [self calculateDirectionFromTouches]);
}

-(direction)calculateDirectionFromTouches {
    NSInteger xDisplacement = self.lastTouch.x-self.startTouch.x;
    NSInteger yDisplacement = self.lastTouch.y-self.startTouch.y;

    float angle = atan2(xDisplacement, yDisplacement);
    int octant = (int)(round(8 * angle / (2 * M_PI) + 8)) % 8;

    return (direction) octant;
}

For simplicity's sake in touchesMoved:, I only log the direction, but you can do what you want with that information (e.g. pass it to a delegate, post a notification with it, etc.). In touchesMoved you'll also need some method to recognize if the swipe is finished or not, but that's not quite relevant to the question so I'll leave that to you. The math in calculateDirectionFromTouches is explained here.

Community
  • 1
  • 1
pasawaya
  • 11,364
  • 7
  • 50
  • 92
2

I finally found the simplest answer, please mark this as the answer if you agree.

If you only have one direction swipe + pan, you just say: [myPanRecogznier requireGestureRecognizerToFail:mySwipeRecognizer];

But if you have two or more swipes, you can't pass an array into that method. For that, there's UIGestureRecognizerDelegate that you need to implement.

For example, if you want to recognize 2 swipes (left and right) and you also want to allow the user to pan up, you define the gesture recognizers as properties or instance variables, and then you set your VC as the delegate on the pan gesture recognizer:

_swipeLeft = [[UISwipeGestureRecognizer alloc] ...]; // use proper init here
_swipeRight = [[UISwipeGestureRecognizer alloc] ...]; // user proper init here
_swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
_swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
_pan = [[UIPanGestureRecognizer alloc] ...]; // use proper init here

_pan.delegate = self;

// then add recognizers to your view

You then implement - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer delegate method, like so:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if (gestureRecognizer == _pan && (otherGestureRecognizer == _swipeLeft || otherGestureRecognizer == _swipeRight)) {
        return YES;
    }

    return NO;
}

This tells the pan gesture recognizer to only work if both left and right swipes fail to be recognize - perfect!

Hopefully in the future Apple will just let us pass an array to the requireGestureRecognizerToFail: method.

Alex the Ukrainian
  • 4,428
  • 3
  • 22
  • 25
1

Use a UIPanGestureRecogizer and detect the swipe directions you care about. see the UIPanGestureRecognizer documentation for details. -rrh

// add pan recognizer to the view when initialized
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panRecognized:)];
[panRecognizer setDelegate:self];
[self addGestureRecognizer:panRecognizer]; // add to the view you want to detect swipe on


-(void)panRecognized:(UIPanGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateBegan) {
        // you might want to do something at the start of the pan
    }

    CGPoint distance = [sender translationInView:self]; // get distance of pan/swipe in the view in which the gesture recognizer was added
    CGPoint velocity = [sender velocityInView:self]; // get velocity of pan/swipe in the view in which the gesture recognizer was added
    float usersSwipeSpeed = abs(velocity.x); // use this if you need to move an object at a speed that matches the users swipe speed
    NSLog(@"swipe speed:%f", usersSwipeSpeed);
    if (sender.state == UIGestureRecognizerStateEnded) {
        [sender cancelsTouchesInView]; // you may or may not need this - check documentation if unsure
        if (distance.x > 0) { // right
            NSLog(@"user swiped right");
        } else if (distance.x < 0) { //left
            NSLog(@"user swiped left");
        }
        if (distance.y > 0) { // down
            NSLog(@"user swiped down");
        } else if (distance.y < 0) { //up
            NSLog(@"user swiped up");
        }
        // Note: if you don't want both axis directions to be triggered (i.e. up and right) you can add a tolerence instead of checking the distance against 0 you could check for greater and less than 50 or 100, etc.
    }
}

CHANHE IN STATEEND CODE WIH THIS

//if YOU WANT ONLY SINGLE SWIPE FROM UP,DOWN,LEFT AND RIGHT

if (sender.state == UIGestureRecognizerStateEnded) {
        [sender cancelsTouchesInView]; // you may or may not need this - check documentation if unsure
        if (distance.x > 0 && abs(distance.x)>abs(distance.y)) { // right
            NSLog(@"user swiped right");
        } else if (distance.x < 0 && abs(distance.x)>abs(distance.y)) { //left
            NSLog(@"user swiped left");
        }
        if (distance.y > 0 && abs(distance.y)>abs(distance.x)) { // down
            NSLog(@"user swiped down");
        } else if (distance.y < 0 && abs(distance.y)>abs(distance.x)) { //up
            NSLog(@"user swiped up");
        }

    } 
GameLoading
  • 6,552
  • 2
  • 31
  • 57
Richie Hyatt
  • 2,044
  • 2
  • 19
  • 14
1

You can add only one diagonal by SwipeGesture, then...

    UISwipeGestureRecognizer *swipeV = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(action)];
    UISwipeGestureRecognizer *swipeH = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(action)];
    swipeH.direction = ( UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight  );
    swipeV.direction = ( UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown );
    [self addGestureRecognizer:swipeH];
    [self addGestureRecognizer:swipeV];
     self.userInteractionEnabled = YES;
Damien Romito
  • 8,463
  • 8
  • 57
  • 71
0
UISwipeGestureRecognizer *Updown=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleGestureNext:)];
        Updown.delegate=self;
        [Updown setDirection:UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp];
        [overLayView addGestureRecognizer:Updown];

        UISwipeGestureRecognizer *LeftRight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleGestureNext:)];
        LeftRight.delegate=self;
        [LeftRight setDirection:UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight];
        [overLayView addGestureRecognizer:LeftRight];
        overLayView.userInteractionEnabled=NO;


-(void)handleGestureNext:(UISwipeGestureRecognizer *)recognizer
{
    NSLog(@"Swipe Recevied");
}
bhavik
  • 1,623
  • 2
  • 12
  • 20
0

It might not be the best solution but you can always specify different UISwipeGestureRecognizer for each swipe direction you want to detect.

In the viewDidLoad method just define the required UISwipeGestureRecognizer:

- (void)viewDidLoad{
    UISwipeGestureRecognizer *recognizerUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeUp:)];
    recognizerUp.direction = UISwipeGestureRecognizerDirectionUp;
    [[self view] addGestureRecognizer:recognizerUp];

    UISwipeGestureRecognizer *recognizerDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeDown:)];
    recognizerDown.direction =  UISwipeGestureRecognizerDirectionDown;
    [[self view] addGestureRecognizer:recognizerDown];
}

Then just implement the respective methods to handle the swipes:

- (void)handleSwipeUp:(UISwipeGestureRecognizer *)sender{
    if (sender.state == UIGestureRecognizerStateEnded){
        NSLog(@"SWIPE UP");
    }
}


- (void)handleSwipeDown:(UISwipeGestureRecognizer *)sender{
    if (sender.state == UIGestureRecognizerStateEnded){
        NSLog(@"SWIPE DOWN");
    }
}