Ios 自定义UIGestureRecognitor并不总是调用其操作方法

Ios 自定义UIGestureRecognitor并不总是调用其操作方法,ios,swift,uigesturerecognizer,Ios,Swift,Uigesturerecognizer,我有一个自定义的UIGestureRecognitor,它可以正确识别所需的手势(2个手指的Z手势),并在touchesEnded中设置状态=.Recognited。问题是,即使手势被识别,它有时调用动作方法,有时不调用。据我所知,这是不确定的 有人知道为什么吗 代码如下: import UIKit import UIKit.UIGestureRecognizerSubclass class ZGestureRecognizer: UIGestureRecognizer { priv

我有一个自定义的UIGestureRecognitor,它可以正确识别所需的手势(2个手指的Z手势),并在
touchesEnded
中设置
状态=.Recognited
。问题是,即使手势被识别,它有时调用动作方法,有时不调用。据我所知,这是不确定的

有人知道为什么吗

代码如下:

import UIKit
import UIKit.UIGestureRecognizerSubclass

class ZGestureRecognizer: UIGestureRecognizer {

    private var topSwipeStartPoint = CGPoint.zero
    private var diagonalSwipeStartPoint = CGPoint.zero
    private var bottomSwipeStartPoint = CGPoint.zero
    private let minDeltaX: CGFloat = 20
    private let minDeltaY: CGFloat = 20
    private var strokePhase = StrokePhase.notStarted
    var trackedTouch: UITouch?


    enum StrokePhase {
        case notStarted
        case topSwipe
        case diagonalSwipe
        case bottomSwipe
    }


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if !(touches.count == 1 || touches.count == 2) {
            state = .failed
            return
        }

        if trackedTouch == nil {
            trackedTouch = touches.min { $0.location(in: self.view?.window).x < $1.location(in: self.view?.window).x }
            strokePhase = .topSwipe
            topSwipeStartPoint = trackedTouch!.locationInWindow!
        }
    }


    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { 
        guard let trackedTouch = trackedTouch,
            trackedTouch.phase != .cancelled,
            trackedTouch.phase != .ended
        else {
           self.state = .failed
           return
        }

        let newPoint = trackedTouch.locationInWindow!
        let previousPoint = trackedTouch.previousLocationInWindow!

        switch strokePhase {
        case .topSwipe:
            if newPoint.x < previousPoint.x { // if we have started moving back to the left
                let deltaX = previousPoint.x - topSwipeStartPoint.x

                if deltaX > minDeltaX && touches.count == 2 {
                   diagonalSwipeStartPoint = previousPoint
                   strokePhase = .diagonalSwipe
                }
                else { // too short right swipe or not 2 touches
                   state = .failed
                   return
                }
             }

        case .diagonalSwipe:
            if newPoint.x > previousPoint.x { // if we have started moving back to the right
                let deltaX = diagonalSwipeStartPoint.x - previousPoint.x
                let deltaY = previousPoint.y - diagonalSwipeStartPoint.y

                if deltaX > minDeltaX && deltaY > minDeltaY && touches.count == 2 {
                   bottomSwipeStartPoint = previousPoint
                   strokePhase = .bottomSwipe
                }
                else { // too short right swipe
                   state = .failed
                   return
                }
             }

        case .bottomSwipe:
            if newPoint.x < previousPoint.x || touches.count != 2 {
               state = .failed
               return
            }

        default: break
        }
    }


   override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
      state = .failed
   }


   override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
      guard strokePhase == .bottomSwipe else {
         state = .failed
         return
      }

      let endPoint = trackedTouch!.locationInWindow!
      let bottomSwipeDeltaX = endPoint.x - bottomSwipeStartPoint.x

      if bottomSwipeDeltaX > minDeltaX {
         state = .recognized
      }
      else {
         state = .failed
      }
   }


   override func reset() {
      strokePhase = .notStarted
      trackedTouch = nil
   }

}
导入UIKit
导入UIKit.UIgestureRecognitizerSubclass
类ZgestureRecognitor:UIgestureRecognitor{
私有变量topSwipeStartPoint=CGPoint.zero
私有变量对角swipestartpoint=CGPoint.zero
私有变量bottomSwipeStartPoint=CGPoint.zero
私人出租minDeltaX:CGFloat=20
私人出租minDeltaY:CGFloat=20
私有变量strokePhase=strokePhase.notStarted
var trackedTouch:UITouch?
枚举冲程相位{
案例未开始
箱子上擦布
格斜击
箱底刷
}
覆盖func touchesStart(touchs:Set,带有事件:UIEvent){
如果!(touchs.count==1 | | touchs.count==2){
状态=。失败
返回
}
如果trackedTouch==nil{
trackedTouch=touchs.min{$0.location(在:self.view?窗口中).x<$1.location(在:self.view?窗口中).x}
冲程相位=.topSwipe
TopsweepStartPoint=trackedTouch!.locationInWindow!
}
}
覆盖func touchesMoved(touches:Set,带有event:UIEvent){
guard let trackedTouch=trackedTouch,
trackedTouch.phase!=已取消,
trackedTouch.phase!=结束
否则{
self.state=.failed
返回
}
让newPoint=trackedTouch.locationInWindow!
让previousPoint=trackedTouch.previousLocationInWindow!
开关冲程相位{
案例1.1:
如果newPoint.xminDeltaX&&Touchs.count==2{
对角点=上一点
strokePhase=.对角线滑动
}
否则{//太短,右击或不触2下
状态=。失败
返回
}
}
案例。对角滑动:
如果newPoint.x>previousPoint.x{//如果我们已经开始往右移动
设deltaX=对角swipestartpoint.x-上一个点.x
设deltaY=previousPoint.y-对角线swipestartpoint.y
如果deltaX>minDeltaX&&deltaY>minDeltaY&&Touchs.count==2{
bottomSwipeStartPoint=上一个点
strokePhase=.bottomSwipe
}
else{//右击太短
状态=。失败
返回
}
}
案例。底部滑动:
如果newPoint.xminDeltaX{
状态=。已识别
}
否则{
状态=。失败
}
}
重写函数重置(){
strokePhase=.notStarted
跟踪触摸=零
}
}

由于手势识别总是以行
state=.recognized
结尾,所以在做适当的手势时,我认为代码不相关,但我很乐意出错。

我模拟了您的代码。我无法得到正确的手势(您没有提到所需的手势)。因此,在我的例子中,state=.recognized从未执行过。然后,我硬编码了touchesend中的state=.recognized,以检查是否每次设置state=.recognized时都调用了action方法。我觉得很好


我发现所有被重写的方法都缺少超级调用。您可以拨打所有超级电话,然后再次检查。此外,将一个日志放在日志中。我认为可能有一个逻辑问题。因为,我不知道触发它所需的手势,我无法调试代码。

我猜您在这个应用程序中编写了一些代码?或者你想让我们想象一下你写了什么?问题是状态设置为。每次执行手势时都会被识别,但动作目标并不是每次都被调用,那么代码的其余部分是如何相关的?据我所知,设置state=.recognized应该会触发目标操作。您是否确定在成功执行手势时(例如,通过在其中添加日志语句),始终会到达将
state
设置为
recognized
的代码路径?需要考虑的其他事项:您从未将
状态设置为其他生命周期状态(
可能的
开始的
更改的
,…)。子类化()的文档建议始终在
可能的
状态下启动。是的,我已经检查了每次成功执行手势时执行是否达到
状态=。已识别
。如果我正确理解了文档,则状态
开始
更改
与连续手势识别器一起使用——我的是离散手势识别器。此外,根据文档,UIKit会在手势失败或成功后将状态设置回
可能
。一种离散手势识别器