UISwipeGestureRecognizer only one direction working
Asked Answered
J

4

6

So Im making a page with pageControl (it's a page with multiple views with dots indicating which page you're in), my code looks like the following in viewDidLoad:

UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
UIView *temp = [[UIView alloc]initWithFrame:self.view.frame];
temp.backgroundColor = [UIColor clearColor];
[temp addGestureRecognizer:swipe];
[self.view addSubview:temp];

And in the swipeAction selector I have:

- (void)swipeAction: (UISwipeGestureRecognizer *)sender{
    NSLog(@"Swipe action called");
    if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
        //Do Something
    }
    else if (sender.direction == UISwipeGestureRecognizerDirectionRight){
        //Do Something Else
    }
}

To my surprise, this method only works when you swipe to the right (i.e. the else if block gets called). When you swipe left, the swipeAction doesn't even get called! This is strange, why does this happen and how should I change my code? Any reply is appreciated. Thanks a lot!

Joanniejoao answered 16/6, 2014 at 17:34 Comment(2)
Is the view with this recognizer inside a navigation controller?Beulabeulah
@SimonGoldeen no it isn't, it's just a normal UIViewJoanniejoao
F
13

There's a couple things you should be aware of here. First, you have to create a gesture for each direction that you want to observe. This isn't a big deal though because you can simple give them the same selector, and it will be just like one gesture for two directions.

UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft;

UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeAction:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;


UIView *temp = [[UIView alloc]initWithFrame:self.view.frame];

temp.backgroundColor = [UIColor clearColor];
[temp addGestureRecognizer:leftSwipe];
[temp addGestureRecognizer:rightSwipe];

[self.view addSubview:temp];

Second, you never specified the direction of the gesture leaving it to default to right (or 1 on the direction enum)

From the documentation:

The default direction is UISwipeGestureRecognizerDirectionRight. See descriptions of UISwipeGestureRecognizerDirection constants for more information.

typedef enum {
   UISwipeGestureRecognizerDirectionRight = 1 << 0,
   UISwipeGestureRecognizerDirectionLeft  = 1 << 1,
   UISwipeGestureRecognizerDirectionUp    = 1 << 2,
   UISwipeGestureRecognizerDirectionDown  = 1 << 3
} UISwipeGestureRecognizerDirection;
Fragmental answered 16/6, 2014 at 17:50 Comment(4)
You don't need two recognizers if you're using the same selector action. You can just OR them together and one recognizer will detect both directions. You would need two recognizers if you wanted to respond differently to a left or right swipe.Adularia
I just tried this before answering...it works. I think that other post is referring to the callback, it will not report the direction in your action.. swipe.direction will still be set to left|right, but it will fire the action correctly in both directions.Adularia
@Adularia I should probably rephrase slightly. But are you able to determine which direction the swipe was with a single gesture?Fragmental
Thanks @0x7fffffff, it works perfectly! At first I used 2 swipe gesture recognizers as well, I guess it didn't work because I also had 2 different selectors corresponding to those recognizers. Then before I posted this question I did some research and I saw some people saying you could have only one gesture recognizer for each type of gesture, so I just sorta combined everything :PJoanniejoao
F
2

Swift 4

let swiper = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeFunction(sender:)))
swiper.direction = UISwipeGestureRecognizer.Direction.right
let swipel = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeFunction(sender:)))
swipel.direction = UISwipeGestureRecognizer.Direction.left
UIView().addGestureRecognizer(swiper)
UIView().addGestureRecognizer(swipel)

@objc func swipeFunction(sender: UISwipeGestureRecognizer) {
    print(sender)
}

you should be able to figure it out from there, you add a UISwipeGestureRecognizer for each direction to the UIView

Fajardo answered 6/10, 2018 at 19:0 Comment(0)
A
1

swipe.direction sets the direction(s) you're recognizing, it doesn't tell you which direction was swiped. Add this line when creating your recognizer:

swipe.direction = UISwipeGestureRecognizerDirectionLeft|UISwipeGestureRecognizerDirectionRight;

If you need to detect which direction was swiped, just use two different Recognizers, one for left and one for right.

Adularia answered 16/6, 2014 at 17:49 Comment(1)
Thanks for the reply @joeld, you're definitely right about it not telling you which direction you swipe, but using your code, it now recognizes every swipe's direction as left, so I guess the only correct way would still be having two swipe gesture recognizers, each detecting a different direction :DJoanniejoao
A
0

I was facing the same issue and found a way by extending the UIPanGestureRecognizer in order to implement a UIDirectionalSwipeGestureRecognizer as follows. The direction of the swipe is available as a public property of the recognizer's instance:

import UIKit

public final class UIDirectionalSwipeGestureRecognizer: UIPanGestureRecognizer {
    
    public private(set) var direction: UISwipeGestureRecognizer.Direction?
    
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
        
        direction = nil
        
        if let touch = touches.first {
            startTimestamp = touch.timestamp
            startLocation = touch.location(in: nil)
        }
    }
    
    public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)
        
        if
            let currentTimestamp = touches.first?.timestamp,
            let startTimestamp = startTimestamp,
            currentTimestamp - startTimestamp > 0.3
        {
            touchesCancelled(touches, with: event)
        }
    }
    
    public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesEnded(touches, with: event)
        
        if
            let endLocation = touches.first?.location(in: nil),
            let startLocation = startLocation,
            startLocation.distance(to: endLocation) > 20
        {
            direction = UISwipeGestureRecognizer.Direction.fromPoint(startLocation, to: endLocation)
        }
    }
    
    // MARK: - Private
    
    private var startTimestamp: TimeInterval?
    private var startLocation: CGPoint?
}

// MARK: - Extensions

public extension CGPoint {
    
    func distanceSquared(to: CGPoint) -> CGFloat {
        (to.x - x) * (to.x - x) + (to.y - y) * (to.y - y)
    }
    
    func distance(to: CGPoint) -> CGFloat {
        sqrt(distanceSquared(to: to))
    }
}

extension UISwipeGestureRecognizer.Direction {
    
    public static func fromPoint(_ startPoint: CGPoint, to endPoint: CGPoint) -> Self {
        let offset = CGSize(width: endPoint.x - startPoint.x, height: endPoint.y - startPoint.y)
        
        if abs(offset.width) > abs(offset.height) {
            return offset.width > 0 ? .right : .left
        } else {
            return offset.height > 0 ? .up : .down
        }
    }
}


Aalii answered 3/6, 2022 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.