UIPanGestureRecognizer - Only vertical or horizontal
IosUigesturerecognizerIos Problem Overview
I have a view that has a UIPanGestureRecognizer
to drag the view vertically. So in the recognizer callback, I only update the y-coordinate to move it. The superview of this view, has a UIPanGestureRecognizer
that will drag the view horizontally, just updating the x-coordinate.
The problem is that the first UIPanGestureRecognizer
is taking the event to move the view vertically, so I can not use the superview gesture.
I have tried
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer;
and both will work, but I don't want that. I want the horizontally to be detected only if the movement is clearly horizontal. So it would be great if the UIPanGestureRecognizer
had a direction property.
How can I achieve this behavior? I find the docs very confusing, so maybe someone can explain it better here.
Ios Solutions
Solution 1 - Ios
Just do this for the vertical pan gesture recognizer, it works for me:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint velocity = [panGestureRecognizer velocityInView:someView];
return fabs(velocity.y) > fabs(velocity.x);
}
And for Swift:
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIPanGestureRecognizer) -> Bool {
let velocity = gestureRecognizer.velocity(in: someView)
return abs(velocity.x) > abs(velocity.y)
}
Solution 2 - Ios
I created a solution with subclassing like in the answer @LocoMike provided, but used the more effective detection mechanism via initial velocity as provided by @Hejazi. I'm also using Swift, but this should be easy to translate to Obj-C if desired.
Advantages over other solutions:
- Simpler and more concise than other subclassing solutions. No additional state to manage.
- Direction detection happens prior to sending Began action, so your pan gesture selector receives no messages if the wrong direction is swiped.
- After initial direction is determined, direction logic is no longer consulted. This results in the generally desired behavior of activating your recognizer if the initial direction is correct, but does not cancel the gesture after it has begun if a user's finger doesn't travel perfectly along the direction.
Here's the code:
import UIKit.UIGestureRecognizerSubclass
enum PanDirection {
case vertical
case horizontal
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction: PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if state == .began {
let vel = velocity(in: view)
switch direction {
case .horizontal where fabs(vel.y) > fabs(vel.x):
state = .cancelled
case .vertical where fabs(vel.x) > fabs(vel.y):
state = .cancelled
default:
break
}
}
}
}
Example of usage:
let panGestureRecognizer = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(handlePanGesture(_:)))
panGestureRecognizer.cancelsTouchesInView = false
self.view.addGestureRecognizer(panGestureRecognizer)
func handlePanGesture(_ pan: UIPanGestureRecognizer) {
let percent = max(pan.translation(in: view).x, 0) / view.frame.width
switch pan.state {
case .began:
...
}
Solution 3 - Ios
I figured it out creating a subclass of UIPanGestureRecognizer
DirectionPanGestureRecognizer:
#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
typedef enum {
DirectionPangestureRecognizerVertical,
DirectionPanGestureRecognizerHorizontal
} DirectionPangestureRecognizerDirection;
@interface DirectionPanGestureRecognizer : UIPanGestureRecognizer {
BOOL _drag;
int _moveX;
int _moveY;
DirectionPangestureRecognizerDirection _direction;
}
@property (nonatomic, assign) DirectionPangestureRecognizerDirection direction;
@end
DirectionPanGestureRecognizer.m:
#import "DirectionPanGestureRecognizer.h"
int const static kDirectionPanThreshold = 5;
@implementation DirectionPanGestureRecognizer
@synthesize direction = _direction;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStateFailed) return;
CGPoint nowPoint = [[touches anyObject] locationInView:self.view];
CGPoint prevPoint = [[touches anyObject] previousLocationInView:self.view];
_moveX += prevPoint.x - nowPoint.x;
_moveY += prevPoint.y - nowPoint.y;
if (!_drag) {
if (abs(_moveX) > kDirectionPanThreshold) {
if (_direction == DirectionPangestureRecognizerVertical) {
self.state = UIGestureRecognizerStateFailed;
}else {
_drag = YES;
}
}else if (abs(_moveY) > kDirectionPanThreshold) {
if (_direction == DirectionPanGestureRecognizerHorizontal) {
self.state = UIGestureRecognizerStateFailed;
}else {
_drag = YES;
}
}
}
}
- (void)reset {
[super reset];
_drag = NO;
_moveX = 0;
_moveY = 0;
}
@end
This will only trigger the gesture if the user starts dragging in the selected behavior. Set the direction property to a correct value and you are all set.
Solution 4 - Ios
I tried to constrain the valid area horizontally with UIPanGestureRecognizer.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
CGPoint velocity = [panGesture velocityInView:panGesture.view];
double radian = atan(velocity.y/velocity.x);
double degree = radian * 180 / M_PI;
double thresholdAngle = 20.0;
if (fabs(degree) > thresholdAngle) {
return NO;
}
}
return YES;
}
Then, only swiping within thresholdAngle degree horizontally can trigger this pan gesture.
Solution 5 - Ios
Swift 3.0 answer: just handles does the vertical gesture
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let pan = gestureRecognizer as? UIPanGestureRecognizer {
let velocity = pan.velocity(in: self)
return fabs(velocity.y) > fabs(velocity.x)
}
return true
}
Solution 6 - Ios
The following solution solved my problem:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer.view isEqual:self.view] && [otherGestureRecognizer.view isEqual:self.tableView]) {
return NO;
}
return YES;
}
This is actually just check if pan is going on main view or tableView.
Solution 7 - Ios
Swift 3 version of Lee's answer for the lazy
import UIKit
import UIKit.UIGestureRecognizerSubclass
enum PanDirection {
case vertical
case horizontal
}
class UIPanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction : PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if state == .began {
let vel = velocity(in: self.view!)
switch direction {
case .horizontal where fabs(vel.y) > fabs(vel.x):
state = .cancelled
case .vertical where fabs(vel.x) > fabs(vel.y):
state = .cancelled
default:
break
}
}
}
}
Solution 8 - Ios
I took Lee Goodrich's answer and extended it as I needed specifically a single direction pan. Use it like this: let pan = PanDirectionGestureRecognizer(direction: .vertical(.up), target: self, action: #selector(handleCellPan(_:)))
I also added some commenting to make it a little clearer what decisions are actually being made.
import UIKit.UIGestureRecognizerSubclass
enum PanVerticalDirection {
case either
case up
case down
}
enum PanHorizontalDirection {
case either
case left
case right
}
enum PanDirection {
case vertical(PanVerticalDirection)
case horizontal(PanHorizontalDirection)
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction: PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if state == .began {
let vel = velocity(in: view)
switch direction {
// expecting horizontal but moving vertical, cancel
case .horizontal(_) where fabs(vel.y) > fabs(vel.x):
state = .cancelled
// expecting vertical but moving horizontal, cancel
case .vertical(_) where fabs(vel.x) > fabs(vel.y):
state = .cancelled
// expecting horizontal and moving horizontal
case .horizontal(let hDirection):
switch hDirection {
// expecting left but moving right, cancel
case .left where vel.x > 0: state = .cancelled
// expecting right but moving left, cancel
case .right where vel.x < 0: state = .cancelled
default: break
}
// expecting vertical and moving vertical
case .vertical(let vDirection):
switch vDirection {
// expecting up but moving down, cancel
case .up where vel.y > 0: state = .cancelled
// expecting down but moving up, cancel
case .down where vel.y < 0: state = .cancelled
default: break
}
}
}
}
}
Solution 9 - Ios
Swift 4.2
The solution is just for only support pan gesture vertically, same as horizontal.
let pan = UIPanGestureRecognizer(target: self, action: #selector(test1))
pan.cancelsTouchesInView = false
panView.addGestureRecognizer(pan)
Solution 1:
@objc func panAction(pan: UIPanGestureRecognizer) {
let velocity = pan.velocity(in: panView)
guard abs(velocity.y) > abs(velocity.x) else {
return
}
}
Solution 2:
[UISwipeGestureRecognizer.Direction.left, .right].forEach { direction in
let swipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction))
swipe.direction = direction
panView.addGestureRecognizer(swipe)
pan.require(toFail: swipe)
}
Then the swipe gesture will swallow the pan gesture. Of course, you don't need to do anything in swipeAction
.
Solution 10 - Ios
Here is a custom pan gesture in Swift 5
U can constraint its direction and the max angle in the direction, you can also constraint its minimum speed in the direction.
enum PanDirection {
case vertical
case horizontal
}
struct Constaint {
let maxAngle: Double
let minSpeed: CGFloat
static let `default` = Constaint(maxAngle: 50, minSpeed: 50)
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction: PanDirection
let constraint: Constaint
init(direction orientation: PanDirection, target: AnyObject, action: Selector, constraint limits: Constaint = Constaint.default) {
direction = orientation
constraint = limits
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
let tangent = tan(constraint.maxAngle * Double.pi / 180)
if state == .began {
let vel = velocity(in: view)
switch direction {
case .horizontal where abs(vel.y)/abs(vel.x) > CGFloat(tangent) || abs(vel.x) < constraint.minSpeed:
state = .cancelled
case .vertical where abs(vel.x)/abs(vel.y) > CGFloat(tangent) || abs(vel.y) < constraint.minSpeed:
state = .cancelled
default:
break
}
}
}
}
call like this:
let pan = PanDirectionGestureRecognizer(direction: .vertical, target: self, action: #selector(self.push(_:)))
view.addGestureRecognizer(pan)
@objc func push(_ gesture: UIPanGestureRecognizer){
if gesture.state == .began{
// command for once
}
}
or
let pan = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(self.push(_:)), constraint: Constaint(maxAngle: 5, minSpeed: 80))
view.addGestureRecognizer(pan)
Solution 11 - Ios
You can find the direction dragging on UIView
through UIPanGestureRecognizer
. Please follow the code.
- (void)viewDidLoad {
[super viewDidLoad];
flipFoward = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doFlipForward:)];
[flipFoward setMaximumNumberOfTouches:1];
[flipFoward setMinimumNumberOfTouches:1];
[flipFoward setDelegate:self];
[self.view addGestureRecognizer:flipFoward];
flipBack = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(doFlipBack:)];
[flipBack setMaximumNumberOfTouches:1];
[flipBack setMinimumNumberOfTouches:1];
[flipBack setDelegate:self];
[self.view addGestureRecognizer:flipBack];
}
#pragma mark -
#pragma mark RESPONDER
-(void)doFlipForward:(UIGestureRecognizer *)aGestureRecognizer{
NSLog(@"doFlipForward");
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateBegan) {
NSLog(@"UIGestureRecognizerStateBegan");
}
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateChanged) {
NSLog(@"UIGestureRecognizerStateChanged");
}
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateEnded) {
NSLog(@"UIGestureRecognizerStateEnded");
}
}
-(void)doFlipBack:(UIGestureRecognizer *)aGestureRecognizer{
NSLog(@"doFlipBack");
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateBegan) {
NSLog(@"UIGestureRecognizerStateBegan1");
}
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateChanged) {
NSLog(@"UIGestureRecognizerStateChanged1");
}
if([(UIPanGestureRecognizer*)aGestureRecognizer state] == UIGestureRecognizerStateEnded) {
NSLog(@"UIGestureRecognizerStateEnded1");
}
}
#pragma mark -
#pragma mark DELEGATE
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
CGSize size = [self.view bounds].size;
CGFloat touchX = [gestureRecognizer locationInView:self.view].x;
if((gestureRecognizer == flipFoward)
&& touchX >= (size.width - 88.0f))
{
return YES;
}
if((gestureRecognizer == flipBack)
&& touchX <= 88.0f)
{
return YES;
}
return NO;
}
Solution 12 - Ios
Here is how I resolved:
First I enabled Simultaneously PanGesture Recognition.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
Then I Isolate Horizontal and Vertical Pan gestures (accumulator is NSMutableArray property):
- (void)verticalPan :(UIPanGestureRecognizer *) sender {
CGPoint touch = [sender translationInView:self];
NSValue *value = [NSValue valueWithCGPoint:touch];
[accumulator addObject:value];
int firstXObjectValue = (int)[[accumulator objectAtIndex:0] CGPointValue].x ;
int lastXObjectValue = (int)[[accumulator lastObject] CGPointValue].x;
int firstYObjectValue = (int)[[accumulator objectAtIndex:0] CGPointValue].y;
int lastYObjectValue = (int)[[accumulator lastObject] CGPointValue].y;
if (abs(lastYObjectValue - firstYObjectValue) < 4 && abs(lastXObjectValue - firstXObjectValue) > 4) {
NSLog(@"Horizontal Pan");
//do something here
}
else if (abs(lastYObjectValue - firstYObjectValue) > 4 && abs(lastXObjectValue - firstXObjectValue) < 4){
NSLog(@"Vertical Pan");
//do something here
}
if (accumulator.count > 3)
[accumulator removeAllObjects];
I pushed an example here:
Solution 13 - Ios
You may use simple panGestureRecognizer
. No need to use
pandirectionregognizer
or stuff. Just use y value of translationInview
Below code move drag view only up and down
- (void)gesturePan_Handle:(UIPanGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gesture translationInView:gesture.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x, recognizer.view.center.y + translation.y);
[gesture setTranslation:CGPointMake(0, 0) inView:gesture.view];
}
}
Solution 14 - Ios
let pangesture = UIPanGestureRecognizer(target: self, action: "dragview:")
yourview.addGestureRecognizer(pangesture)
func dragview(panGestureRecognizer:UIPanGestureRecognizer)
{
let touchlocation = panGestureRecognizer.locationInView(parentview)
yourview.center.y = touchlocation.y //x for horizontal
}
Solution 15 - Ios
- (void)dragAction:(UIPanGestureRecognizer *)gesture{
UILabel *label = (UILabel *)gesture.view;
CGPoint translation = [gesture translationInView:label];
label.center = CGPointMake(label.center.x + translation.x,
label.center.y + 0);
[gesture setTranslation:CGPointZero inView:label];}
I created PanGestureRecognizer @selector action method for the object that needed only Horizontal scrolling.
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(smileyDragged:)];
[buttonObject addGestureRecognizer:gesture];
Solution 16 - Ios
Swift way
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
return isVerticalGesture(panGestureRecognizer)
}
return false
}
private func isVerticalGesture(_ recognizer: UIPanGestureRecognizer) -> Bool {
let translation = recognizer.translation(in: superview!)
if fabs(translation.y) > fabs(translation.x) {
return true
}
return false
}
Solution 17 - Ios
For all you Swift users out there, this will do the job :)
import Foundation
import UIKit.UIGestureRecognizerSubclass
class DirectionPanGestureRecognizer: UIPanGestureRecognizer {
let kDirectionPanThreshold = CGFloat(5)
var drag = true
var moveX = CGFloat(0)
var moveY = CGFloat(0)
override init(target: AnyObject, action: Selector) {
super.init(target: target, action: action)
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
if state == .Failed {
return
}
let nowPoint = touches.anyObject()?.locationInView(view)
let prevPoint = touches.anyObject()?.previousLocationInView(view)
moveX += prevPoint!.x - nowPoint!.x
moveY += prevPoint!.y - nowPoint!.y
if !drag {
if abs(moveX) > kDirectionPanThreshold {
state = .Failed
} else {
drag = true
}
}
}
override func reset() {
super.reset()
moveX = 0
moveY = 0
drag = false
}
}
Solution 18 - Ios
I took an excellent answer by Lee Goodrich and ported to Swift 3
import UIKit
import UIKit.UIGestureRecognizerSubclass
enum PanDirection {
case vertical
case horizontal
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction : PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if state == .began {
let vel = velocity(in: self.view!)
switch direction {
case .horizontal where fabs(vel.y) > fabs(vel.x):
state = .cancelled
case .vertical where fabs(vel.x) > fabs(vel.y):
state = .cancelled
default:
break
}
}
}
}
Solution 19 - Ios
I would love to share my approach because all other approaches are based on either UIGestureRecognizerDelegate
or subclassing UIPanGestureRecognizer
.
My approach is based on runtime and swizzling. I'm not 100% sure about this approach, but you can test and improve it yourself.
Set the direction of any UIPanGestureRecognizer
with just one line of code:
UITableView().panGestureRecognizer.direction = UIPanGestureRecognizer.Direction.vertical
use pod 'UIPanGestureRecognizerDirection'
or the code:
public extension UIPanGestureRecognizer {
override open class func initialize() {
super.initialize()
guard self === UIPanGestureRecognizer.self else { return }
func replace(_ method: Selector, with anotherMethod: Selector, for clаss: AnyClass) {
let original = class_getInstanceMethod(clаss, method)
let swizzled = class_getInstanceMethod(clаss, anotherMethod)
switch class_addMethod(clаss, method, method_getImplementation(swizzled), method_getTypeEncoding(swizzled)) {
case true:
class_replaceMethod(clаss, anotherMethod, method_getImplementation(original), method_getTypeEncoding(original))
case false:
method_exchangeImplementations(original, swizzled)
}
}
let selector1 = #selector(UIPanGestureRecognizer.touchesBegan(_:with:))
let selector2 = #selector(UIPanGestureRecognizer.swizzling_touchesBegan(_:with:))
replace(selector1, with: selector2, for: self)
let selector3 = #selector(UIPanGestureRecognizer.touchesMoved(_:with:))
let selector4 = #selector(UIPanGestureRecognizer.swizzling_touchesMoved(_:with:))
replace(selector3, with: selector4, for: self)
}
@objc private func swizzling_touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
self.swizzling_touchesBegan(touches, with: event)
guard direction != nil else { return }
touchesBegan = true
}
@objc private func swizzling_touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
self.swizzling_touchesMoved(touches, with: event)
guard let direction = direction, touchesBegan == true else { return }
defer {
touchesBegan = false
}
let forbiddenDirectionsCount = touches
.flatMap({ ($0.location(in: $0.view) - $0.previousLocation(in: $0.view)).direction })
.filter({ $0 != direction })
.count
if forbiddenDirectionsCount > 0 {
state = .failed
}
}
}
public extension UIPanGestureRecognizer {
public enum Direction: Int {
case horizontal = 0
case vertical
}
private struct UIPanGestureRecognizerRuntimeKeys {
static var directions = "\(#file)+\(#line)"
static var touchesBegan = "\(#file)+\(#line)"
}
public var direction: UIPanGestureRecognizer.Direction? {
get {
let object = objc_getAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.directions)
return object as? UIPanGestureRecognizer.Direction
}
set {
let policy = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.directions, newValue, policy)
}
}
fileprivate var touchesBegan: Bool {
get {
let object = objc_getAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.touchesBegan)
return (object as? Bool) ?? false
}
set {
let policy = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &UIPanGestureRecognizerRuntimeKeys.touchesBegan, newValue, policy)
}
}
}
fileprivate extension CGPoint {
var direction: UIPanGestureRecognizer.Direction? {
guard self != .zero else { return nil }
switch fabs(x) > fabs(y) {
case true: return .horizontal
case false: return .vertical
}
}
static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
}
}
Solution 20 - Ios
I tried this: which worked for me as per the question describes
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPanGestureRecognizer {
return true
} else {
return false
}
}
Solution 21 - Ios
SWIFT 4.2
I went further and make a direction Pan Gesture:
enum PanDirection {
case up
case left
case right
case down
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
fileprivate let direction: PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
guard state != .failed else { return }
let vel = velocity(in: view)
let velocities: [PanDirection: CGFloat]
= [.up: -vel.y,
.left: -vel.x,
.right: vel.x,
.down: vel.y]
let sortedKeys = velocities.sorted { $0.1 < $1.1 }
if let key = sortedKeys.last?.key,
key != direction {
state = .cancelled
}
}
}
(Used: https://github.com/fastred/SloppySwiper and https://stackoverflow.com/a/30607392/5790492)
Solution 22 - Ios
PanGestureRecognizer
interface contains the following definitions:
unsigned int _canPanHorizontally:1;
unsigned int _canPanVertically:1;
I didn't check this, but maybe it's accessible via subclass.