Setting direction for UISwipeGestureRecognizer
Asked Answered
F

12

131

I want to add simple swipe gesture recognition to my view based iPhone project. Gestures in all directions (right, down, left, up) should be recognized.

It is stated in the docs for UISwipeGestureRecognizer:

You may specify multiple directions by specifying multiple UISwipeGestureRecognizerDirection constants using bitwise-OR operands. The default direction is UISwipeGestureRecognizerDirectionRight.

However for me it doesn't work. When all four directions are OR'ed only left and right swipes are recognized.

- (void)viewDidLoad {
    UISwipeGestureRecognizer *recognizer;

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionUp)];
    [[self view] addGestureRecognizer:recognizer];
    [recognizer release]; 
    [super viewDidLoad];
}

-(void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer {
    NSLog(@"Swipe received.");
}

I fixed this with adding four recognizers to the view but I'm curious to know why didn't it work as advertised in docs?

- (void)viewDidLoad {
    UISwipeGestureRecognizer *recognizer;

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [[self view] addGestureRecognizer:recognizer];
    [recognizer release];

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionUp)];
    [[self view] addGestureRecognizer:recognizer];
    [recognizer release];

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionDown)];
    [[self view] addGestureRecognizer:recognizer];
    [recognizer release];

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    [[self view] addGestureRecognizer:recognizer];
    [recognizer release];

    [super viewDidLoad];
}

-(void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer {
    NSLog(@"Swipe received.");
}
Frogfish answered 23/7, 2010 at 14:39 Comment(1)
it is entirely unrelated , but [super viewDidLoad]; should be first statement in - (void)viewDidLoadMerkel
R
115

Seems like there is a bug. You can specify the allowed direction(s) as you did. But when you try to access the actual direction that triggered the swipe in the action selector method you still get the bit mask you originally set (for the allowed directions).

This means that checks for the actual direction will always fail when more than 1 direction is allowed. You can see it for yourself quite easily when you output the value of 'direction' in the selector method (ie -(void)scrollViewSwiped:(UISwipeGestureRecognizer *)recognizer).

Filed a bug report (#8276386) to Apple.

[Update] I got an answer from Apple saying that the behavior works as was intended.

So for example in a table view you can swipe left or right in a table view cell to trigger 'delete' (this would have directions of the swipe gesture set to left and right)

This means that the original workaround is the way it's supposed to be used. The direction property can only be used to get the gestures recognized correctly, but not in the method performed on a successful recognition to compare for the actual direction that triggered the recognition.

Roseannroseanna answered 5/8, 2010 at 13:52 Comment(11)
I'm willing to bet that nearly everyone that first uses the swipe gesture recognizer makes the exact same incorrect assumption about how the direction property should work.Vicarial
It's silly to have to create 4 different recognizers to track up, down, left and right swipes, but there you are.Vicarial
wow that kinda sucks, not a huge deal but seems like something they could have addedLamplighter
Their header file says: @property(nonatomic) UISwipeGestureRecognizerDirection direction; // default is UISwipeGestureRecognizerDirectionRight. the desired direction of the swipe. multiple directions may be specifiedCachinnate
@Harkonian #1: "I'm willing to bet..." - I'm not a counter example to your assertion. :)Folksy
Agreed that this seems like a strange implementation on Apple's part. You should be able to specify multiple directions and then test for one of those directions.Phlyctena
"The direction property can only be used to get the gestures recognized correctly" is not correct. I have UISwipeGestureRecognizerDirectionUp|UISwipeGestureRecognizerDirectionRight|UISwipeGestureRecognizerDirectionLeft but it did not even trigger the method on swiping upKathrinkathrine
Apple: @property(nonatomic, readonly) UISwipeGestureRecognizerDirection recognizedDirection;Blister
that's bullshit, if they intended this to work this way they would NOT have used 4 constants that can be used together, they would have used 0,1,2,3 instead of 1,2,4,8.Shevat
@PizzaiolaGorgonzola They must have updated the documentation because it currently shows 0,1,2,3 not 1,2,4,8: developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/…Urn
@Brandon, I guess they updated it again. It's now 1, 2, 4, 8 (according to your link).Subversive
V
25

I noticed that left/right and up/down gestures work together in pairs, so you only need to specify two gesture recognizers. And the docs do seem to be wrong.

Valoniah answered 28/7, 2010 at 22:2 Comment(1)
This is the correct solution. 2 GRs, one for up and down / one for left and right. Bingo!Androus
T
22

Well that sucks, I solved the problem by adding 2 gestures like Lars mentioned and that worked perfectly...

1) Left/Right 2) Up/Down

  

UISwipeGestureRecognizer *swipeLeftRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    [swipeLeftRight setDirection:(UISwipeGestureRecognizerDirectionRight | UISwipeGestureRecognizerDirectionLeft )];
    [self.view addGestureRecognizer:swipeLeftRight];

    UISwipeGestureRecognizer *swipeUpDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    [swipeUpDown setDirection:(UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown )];
    [self.view addGestureRecognizer:swipeUpDown];
Tombaugh answered 23/12, 2011 at 10:32 Comment(0)
D
13
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipe:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[self.view addGestureRecognizer:recognizer];
[recognizer release];

recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipe:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[self.view addGestureRecognizer:recognizer];
[recognizer release];

Now this is the didSwipe function

- (void) didSwipe:(UISwipeGestureRecognizer *)recognizer{
      if([recognizer direction] == UISwipeGestureRecognizerDirectionLeft){
          //Swipe from right to left
          //Do your functions here
      }else{
          //Swipe from left to right
          //Do your functions here
      }
 }
Dragon answered 18/9, 2012 at 8:5 Comment(0)
I
5

If your using Xcode 4.2 you can add Gesture Recognizers @ the storyboard and then link the GUI Gesture Recognizers to IBActions.

You can find the Gesture Recognizers in the Object Library of the Utility Pane (The bottom of the Right pane).

Then its just a matter of Control-dragging to the appropriate action.

Ingrowing answered 11/1, 2012 at 23:17 Comment(0)
D
5

If you want it to detect all four directions, you'll need to create four instances, as you did in your work-around.

Here's Why: The same instance of UISwipeGestureRecognizer that you create is the instance that gets passed to the selector as sender. So if you set it to recognize all four directions, it will return true for sgr.direction == xxx where xxx is any one of the four directions.

Here's an alternative work-around that involves less code (assumes ARC use):

for(int d = UISwipeGestureRecognizerDirectionRight; d <= UISwipeGestureRecognizerDirectionDown; d = d*2) {
    UISwipeGestureRecognizer *sgr = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:)];
    sgr.direction = d;
    [self.view addGestureRecognizer:sgr];
}
Donaldson answered 24/7, 2012 at 20:11 Comment(0)
I
3

Swift 2.1

I had to use the following

    for var x in [
        UISwipeGestureRecognizerDirection.Left,
        UISwipeGestureRecognizerDirection.Right,
        UISwipeGestureRecognizerDirection.Up,
        UISwipeGestureRecognizerDirection.Down
    ] {
        let r = UISwipeGestureRecognizer(target: self, action: "swipe:")
        r.direction = x
        self.view.addGestureRecognizer(r)
    }
Illomened answered 14/3, 2016 at 18:48 Comment(0)
M
2

Here is a code sample for UISwipeGestureRecognizer usage. Note comments.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //add gesture recognizer. The 'direction' property of UISwipeGestureRecognizer only sets the allowable directions. It does not return to the user the direction that was actaully swiped. Must set up separate gesture recognizers to handle the specific directions for which I want an outcome.
    UISwipeGestureRecognizer *gestureRight;
    UISwipeGestureRecognizer *gestureLeft;
    gestureRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRight:)];//direction is set by default.
    gestureLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeft:)];//need to set direction.
    [gestureLeft setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    //[gesture setNumberOfTouchesRequired:1];//default is 1
    [[self view] addGestureRecognizer:gestureRight];//this gets things rolling.
    [[self view] addGestureRecognizer:gestureLeft];//this gets things rolling.
}

swipeRight and swipeLeft are methods that you use to perform specific activities based on left or right swiping. For example:

- (void)swipeRight:(UISwipeGestureRecognizer *)gesture
{
    NSLog(@"Right Swipe received.");//Lets you know this method was called by gesture recognizer.
    NSLog(@"Direction is: %i", gesture.direction);//Lets you know the numeric value of the gesture direction for confirmation (1=right).
    //only interested in gesture if gesture state == changed or ended (From Paul Hegarty @ standford U
    if ((gesture.state == UIGestureRecognizerStateChanged) ||
    (gesture.state == UIGestureRecognizerStateEnded)) {

    //do something for a right swipe gesture.
    }
}
Miniaturist answered 28/6, 2012 at 21:39 Comment(0)
P
2
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");
        //Left
        //Right
        //Top
        //Bottom
    }
Prom answered 22/9, 2014 at 12:21 Comment(0)
K
1

hmm, strange, it works perfect for me, I do exactly same thing

think you should try look at

UIGestureRecognizerDelegate method

- (BOOL)gestureRecognizerShouldBegin:(UISwipeGestureRecognizer *)gestureRecognizer {
   // also try to look what's wrong with gesture
   NSLog(@"should began gesture %@", gestureRecognizer);
   return YES;
}

in logs you must see something like:

should began gesture ; target= <(action=actionForUpDownSwipeGestureRecognizer:, target=)>; direction = up,down,left,right>

Kinder answered 5/9, 2011 at 23:16 Comment(1)
What doesn't work? gestureRecognizerShouldBegin: works fine.Nochur
Z
1

use this, it should be the bit operation

   gesture.direction & UISwipeGestureRecognizerDirectionUp || 
   gesture.direction & UISwipeGestureRecognizerDirectionDown
Zlatoust answered 14/3, 2012 at 4:13 Comment(0)
L
0

This was driving me crazy. I finally figured out a reliable way to have multiple swipeGestureRecognizers.

It appears there is a bug in iOS if the name of your "action" selector is the same across multiple swipeGestureRecognizers. If you just name them differently, e.g. handleLeftSwipeFrom and handleRightSwipeFrom, everything works.

UISwipeGestureRecognizer *recognizer;

recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipeFrom:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[[self view] addGestureRecognizer:recognizer];
[recognizer release];

recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipeFrom:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[[self view] addGestureRecognizer:recognizer];
[recognizer release];
Lezley answered 24/10, 2012 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.