在 iOS 中检测 PAN 手势的方向

Posted

技术标签:

【中文标题】在 iOS 中检测 PAN 手势的方向【英文标题】:Detecting the direction of PAN gesture in iOS 【发布时间】:2012-08-02 12:19:48 【问题描述】:

在我的应用中有一个图像视图。我正在向该图像视图添加 PAN 手势。

这工作正常。

图像视图处于横向模式。

我想在用户向右平移时增加标签计数,并在用户向左平移时减少该计数。

我搜索了很多,但没有找到解决方案。

谁能帮助我如何检测用户平移的方向(左/右)?

【问题讨论】:

“我正在向该图像视图添加 PAN 手势。”你的意思是手势识别器 我在谷歌上搜索了一下,第一次点击很好地解释了一切google.com/… 见***.com/a/27130070/1418457 【参考方案1】:

在手势识别器的目标选择器中,使用- (CGPoint)velocityInView:(UIView *)view;

- (void)panRecognized:(UIPanGestureRecognizer *)rec

    CGPoint vel = [rec velocityInView:self.view];
    if (vel.x > 0)
    
        // user dragged towards the right
        counter++;
    
    else
    
        // user dragged towards the left
        counter--;
    

P.s.:直到大约我才知道这种方法。 3 分钟前。 Google 的第一个热门产品是 Apple 官方文档。

【讨论】:

0.0 的速度根本不是任何方向,因此更适合明确(如果 vel.x > 0.0 否则如果 vel.x 【参考方案2】:

类似这样的:

- (void)pan:(UIPanGestureRecognizer *)sender


    typedef NS_ENUM(NSUInteger, UIPanGestureRecognizerDirection) 
        UIPanGestureRecognizerDirectionUndefined,
        UIPanGestureRecognizerDirectionUp,
        UIPanGestureRecognizerDirectionDown,
        UIPanGestureRecognizerDirectionLeft,
        UIPanGestureRecognizerDirectionRight
    ;

    static UIPanGestureRecognizerDirection direction = UIPanGestureRecognizerDirectionUndefined;

    switch (sender.state) 

        case UIGestureRecognizerStateBegan: 

            if (direction == UIPanGestureRecognizerDirectionUndefined) 

                CGPoint velocity = [sender velocityInView:recognizer.view];

                BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x);

                if (isVerticalGesture) 
                    if (velocity.y > 0) 
                        direction = UIPanGestureRecognizerDirectionDown;
                     else 
                        direction = UIPanGestureRecognizerDirectionUp;
                    
                

                else 
                    if (velocity.x > 0) 
                        direction = UIPanGestureRecognizerDirectionRight;
                     else 
                        direction = UIPanGestureRecognizerDirectionLeft;
                    
                
            

            break;
        

        case UIGestureRecognizerStateChanged: 
            switch (direction) 
                case UIPanGestureRecognizerDirectionUp: 
                    [self handleUpwardsGesture:sender];
                    break;
                
                case UIPanGestureRecognizerDirectionDown: 
                    [self handleDownwardsGesture:sender];
                    break;
                
                case UIPanGestureRecognizerDirectionLeft: 
                    [self handleLeftGesture:sender];
                    break;
                
                case UIPanGestureRecognizerDirectionRight: 
                    [self handleRightGesture:sender];
                    break;
                
                default: 
                    break;
                
            
        

        case UIGestureRecognizerStateEnded: 
            direction = UIPanGestureRecognizerDirectionUndefined;   
            break;
        

        default:
            break;
    



- (void)handleUpwardsGesture:(UIPanGestureRecognizer *)sender

    NSLog(@"Up");


- (void)handleDownwardsGesture:(UIPanGestureRecognizer *)sender

    NSLog(@"Down");


- (void)handleLeftGesture:(UIPanGestureRecognizer *)sender

    NSLog(@"Left");


- (void)handleRightGesture:(UIPanGestureRecognizer *)sender

    NSLog(@"Right");

【讨论】:

【参考方案3】:

我之前在 Swift 中的回答

public enum Direction: Int 
    case Up
    case Down
    case Left
    case Right

    public var isX: Bool  return self == .Left || self == .Right 
    public var isY: Bool  return !isX 


public extension UIPanGestureRecognizer 

    public var direction: Direction? 
        let velocity = velocityInView(view)
        let vertical = fabs(velocity.y) > fabs(velocity.x)
        switch (vertical, velocity.x, velocity.y) 
        case (true, _, let y) where y < 0: return .Up
        case (true, _, let y) where y > 0: return .Down
        case (false, let x, _) where x > 0: return .Right
        case (false, let x, _) where x < 0: return .Left
        default: return nil
        
    

【讨论】:

我相信您颠倒了 .Up 和 .Down,但除此之外,谢谢!你是救生员! @AdamWaite: 将答案更新为@available(swift, deprecated: 4.2, rename: "abs") public func fabs(_ x: T) -> T where T : FloatingPoint跨度> 【参考方案4】:

这是一个清理后的 Swift 5 版本,带有使用示例:

public enum PanDirection: Int 
    case up, down, left, right
    public var isVertical: Bool  return [.up, .down].contains(self) 
    public var isHorizontal: Bool  return !isVertical 


public extension UIPanGestureRecognizer 

   var direction: PanDirection? 
        let velocity = self.velocity(in: view)
        let isVertical = abs(velocity.y) > abs(velocity.x)
        switch (isVertical, velocity.x, velocity.y) 
        case (true, _, let y) where y < 0: return .up
        case (true, _, let y) where y > 0: return .down
        case (false, let x, _) where x > 0: return .right
        case (false, let x, _) where x < 0: return .left
        default: return nil
        
    



@IBAction func pan(_ recognizer: UIPanGestureRecognizer)         
    if let direction = recognizer.direction 
        if direction.isVertical 
            //do what you want when pan is vertical
         else if direction == .left 
            //do what you want when pan is left
        
    

【讨论】:

也许更好地进行一些更改以不需要返回选项,例如在where 中使用&gt;=。我想不出一个平移手势返回“无方向”的情况。【参考方案5】:

在 Swift 3 上重写 Adam Waite 版本

public enum PanDirection: Int 
    case up,
    down,
    left,
    right

    public var isX: Bool 
        return self == .left || self == .right
    

    public var isY: Bool 
        return !isX
    


extension UIPanGestureRecognizer 
    var direction: PanDirection? 
        let velocity = self.velocity(in: view)
        let vertical = fabs(velocity.y) > fabs(velocity.x)
        switch (vertical, velocity.x, velocity.y) 
        case (true, _, let y):
            return y < 0 ? .up : .down

        case (false, let x, _):
            return x > 0 ? .right : .left
        
    

【讨论】:

这个版本有一个严重的错误。如果 y == 0,它将返回 .down。如果 x==0,它将返回 .left。在这些情况下,您应该返回一个 nil PanDirection,就像 Adam Waite 的版本一样。 我认为您不需要在direction中返回选项【参考方案6】:

请注意,上面亚当的 Swift 版本有一个错误。如果 y 0 应该返回 .down (它们是相反的)。

所以:

//MARK: - Direction
internal enum Direction 
    case up
    case down
    case left
    case right


//MARK: - UIPanGestureRecognizer
internal extension UIPanGestureRecognizer 
    internal var direction: Direction? 
        let velocity = velocityInView(view)
        let isVertical = fabs(velocity.y) > fabs(velocity.x)

        switch (isVertical, velocity.x, velocity.y) 
            case (true, _, let y) where y < 0: return .up
            case (true, _, let y) where y > 0: return .down
            case (false, let x, _) where x > 0: return .right
            case (false, let x, _) where x < 0: return .left
            default: return nil
        
    

【讨论】:

这里是 Swift 中的菜鸟,假设我想在向上滑动时调用一个方法,我该怎么做? @贾斯汀斯坦利 添加您的平移手势识别器,并在它的其中一个代表中进行平移,或者执行以下操作:如果识别器.direction == .up 做某事 或打开识别器.direction 并将其放在外壳中.up.【参考方案7】:

velocity 对我来说太敏感了,用户很容易将点击屏幕与平移一小部分混合在一起。

所以我使用自定义UIPanGestureRecognizer斯威夫特 5

enum Orientation
    case lhs, rhs, `default`




class HPan: UIPanGestureRecognizer 

    var first: CGPoint?
    var orient = .default

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) 
        super.touchesBegan(touches, with: event)

        state = .began
        first = touches.first?.location(in: view?.window)
    


    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) 
        super.touchesMoved(touches, with: event)
        if state == .cancelled
            return
        
        if let location = touches.first?.location(in: view?.window), let begin = first
          let magicDistance: CGFloat = 20
          if location.x - begin.x > magicDistance
               orient = .rhs
               state = .ended
            
          else if begin.x - location.x > magicDistance
               orient = .lhs
               state = .ended
            
        

    



    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) 
        super.touchesEnded(touches, with: event)
        state = .ended
    



    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) 
        super.touchesCancelled(touches, with: event)
        state = .cancelled
    


    override func reset() 
        super.reset()

        state = .possible
        isRight = false
        orient = .default
    

这样调用:

@objc func push(_ gesture: RightPan)
        if gesture.state == UIGestureRecognizer.State.ended
            switch gesture.orient
                case .lhs:
                     counter -= 1
                case .rhs:
                     counter += 1
                default:
                    ()        
            
        

    

【讨论】:

以上是关于在 iOS 中检测 PAN 手势的方向的主要内容,如果未能解决你的问题,请参考以下文章

给定触摸点列表,如何检测摇晃手势?

使用手势的 iOS Phonegap 滑动检测

如何检测同时手势的结束? (iOS)

iOS 轻击触摸和手势的检测

iOS 14迎来健身交互新方式 支持手势与身体姿势检测

检测滑动手势 iOS - Cordova/Phonegap Javascript