UIPanGestureRecognizer - 只允许垂直或水平移动

157
我有一个视图,带有一个 UIPanGestureRecognizer 以垂直方向拖动视图。因此,在手势识别回调中,我只更新 y 坐标以移动它。这个视图的 superview 有一个 UIPanGestureRecognizer ,将视图水平拖动,只更新 x 坐标。
问题是第一个 UIPanGestureRecognizer 正在接收事件以使视图垂直移动,所以我不能使用 superview 的手势。
我已经尝试过:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
 shouldRecognizeSimultaneouslyWithGestureRecognizer:
                            (UIGestureRecognizer *)otherGestureRecognizer;

两种方法都可以使用,但我不想这么做。我只希望在移动明显是水平方向时才检测到水平方向。因此,如果UIPanGestureRecognizer有一个方向属性就太好了。

如何实现这种行为?我觉得文档写得很混乱,所以也许有人可以在这里更好地解释一下。


如果你已经找到了解决方案,回答自己的问题并接受答案是可以的。 - jtbandes
@JoeBlow 真的吗?那么,也许你创建了滑动手势类别来接收翻译和手势速度? - Roman Truba
2
我不明白你在说什么。如果你想要检测水平滑动,这完全是内置于操作系统中的。所有的工作都已经为你完成了。你什么也不需要做! :) 只需将此示例中的两行代码粘贴即可.. http://stackoverflow.com/a/20988648/294884 请注意,您可以选择“仅左”、“仅右”或“两者皆可”。 - Fattie
22个回答

2

您可以通过UIPanGestureRecognizerUIView上拖动以查找方向。 请按照以下代码操作。

 - (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;
}

实际上这不是一个好的解决方案,因为只有左边的那88个点能够移动。 - Borut Tomazin

1
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 
}

1
- (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];}

我为需要仅水平滚动的对象创建了PanGestureRecognizer @selector操作方法。
 UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(smileyDragged:)];
    [buttonObject addGestureRecognizer:gesture];

1
这是我的解决方法:
首先,我启用了同时的PanGesture识别。
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

return YES;

然后我会分离水平和垂直的Pan手势(累加器是NSMutableArray属性):
- (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];

我在这里提供了一个示例:

在滚动视图中添加自定义面板


1

您可以使用简单的panGestureRecognizer,无需使用pandirectionregognizer等复杂的东西。只需使用translationInview的y值即可实现下拉上拉操作

以下代码仅将拖动视图向上或向下移动

- (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];
    }
}

这段代码只是简单地平移视图,没有实现方向锁定。 - zakishaheen

1

Swift方式

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
}

0

对于所有使用Swift的用户,这将完成工作 :)

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
}




}

0
我尝试过这个:它按照问题描述对我有效。
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer is UIPanGestureRecognizer {
        return true
    } else {
        return false
    }
}

0

我将Lee Goodrich的优秀答案移植到了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

            }

        }
    }
}

0

SWIFT 4.2

我进一步制作了一个方向Pan手势:

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
        }
    }
}

(使用:https://github.com/fastred/SloppySwiperhttps://dev59.com/lGw05IYBdhLWcg3wqzms#30607392


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接