您可以定义自己的自定义手势识别器

如果要实现Apple尚未提供的手势,则可以自定义手势。 但是,他们建议使用标准手势,因为用户已经熟悉它们,因此请花一些时间考虑自定义手势是否正确。 自定义手势通常会出现在游戏中,因此可能是添加您的创意手势识别器的好地方。

您可以定义两种类型的自定义手势识别器:

  • 离散 -当手势涉及特定事件模式时。 示例:识别对勾,圆圈或V形(请参见下面的示例)。 由于它们可以具有多个状态,因此比连续状态更易于实现。
  • 连续 -当手势未遵循预定义的模式时,当您希望收集用户输入并且没有失败情况时。 示例:在屏幕上识别用户的图纸。 通常,这需要更多的精力来实施。

离散手势识别器示例

出于学习目的,我们选择了一个识别正确的人字形的简单示例,然后打开图片库。 在实际应用中,您可能需要研究其他标准手势,以使用加速器来打开照片库。

状态机

在进入它之前,我们应该了解状态机。 这将是离散手势(特定模式)的识别器,因此我们将研究仅在这种情况下如何管理过渡。 如果您想了解有关连续手势的状态机的更多信息,请查阅Apple的文档。

手势识别器始终以可能的状态开始,这告诉我们已经准备好处理事件。 根据用户的输入,我们可以以失败 (不成功)状态或结束 (成功)状态结束 。 您必须检查用户是否遵循您的模式,并且应与该模式有任何偏差,将状态设置为“失败”。 如果用户确实遵循了预期的模式,则UIKit将调用与目标对象关联的action方法。

当当前事件被系统事件(例如接听电话)打断时,我们也具有取消状态。 您还可以自定义应用中可能发生的取消情况。 这是为了确保我们不会执行用户不希望执行的任务。

实现reset()方法也很重要,该方法将确保我们准备通过返回起始值来处理新的事件序列。

图片来源

自定义手势识别器

要使手势成功,您拥有的条件越多,它将越精确。 对于此示例,我们希望用户仅使用一个手指进行手势,从左到右进行向下笔触,然后从右至左进行向下笔触。 这些条件为边缘情况留下了开放的可能性,但就本示例而言,我们不会对此进行过多研究。

我们要做的是首先创建自定义手势识别器,然后将其添加到我们的视图中。

1.制作自定义手势识别器

创建一个新的swift文件,并导入UIKitUIKit.UIGestureRecognizerSubclass ,这是一个头文件,定义了实现自定义手势识别器必须重写的方法和属性。

 import UIKit 
import UIKit.UIGestureRecognizerSubclass

在我们的例子中,我们将必须实现touchesBegintouchesMovedtouchesEndedtouchesCancelledreset

为了跟踪人字形的相位,我们可以使用在自定义手势识别器类之前定义的enum 。 在大多数情况下,您还希望拥有一个notStartedinitialPoint ,然后可以根据自己的手势识别器定义其他阶段。 对于人字形,我们将有一个rightDownStrokeleftDownStroke

 enum SymbolPhase { 
case notStarted
case initialPoint
case rightDownStroke
case leftDownStroke
}

然后,我们开始定义我们的课程。 我们需要声明一个strokePhase状态,其初始状态为notStarted ,初始接触点(目前为零)和UITouch类型的trackedTouch 。 UITouch是一个对象,它表示屏幕上发生的触摸的位置,大小,移动和作用力,并且是我们自定义节日识别器的重要组成部分。

 // Start of the class 
class CustomGestureRecognizer : UIGestureRecognizer {
var strokePhase : SymbolPhase = .notStarted
var initialTouchPoint : CGPoint = CGPoint.zero
var trackedTouch : UITouch? = nil
//Continue below

我们覆盖的第一个功能是touchesBegan ,在这里我们检查是否仅在屏幕上只有手指时才启动手势识别器。 如果存在,则将strokePhase更改为initialPoint ,并将跟踪的触摸的位置保存到我们的initialTouchPoint

 override func touchesBegan(_ touches: Set, with event: UIEvent) { 
super.touchesBegan(touches, with: event)
if touches.count != 1 {
self.state = .failed
}
// Capture the first touch and store some information about it.
if self.trackedTouch == nil {
self.trackedTouch = touches.first
self.strokePhase = .initialPoint
self.initialTouchPoint = (self.trackedTouch?.location(in: self.view))!
} else {
// Ignore all but the first touch.
for touch in touches {
if touch != self.trackedTouch {
self.ignore(touch, for: event)
}
}
}
}
//Continue below

接下来,我们实现touchesMoved ,它保持了手势跟踪的主要逻辑。 我们确保只跟踪第一次触摸,并保存新点和上一点,以便我们可以进行比较。 我们知道,在initialPoint状态之后,我们应该继续rightDownStroke ,在rightDownStroke我们应该有一个leftDownStroke 。 因此,我们检查我们处于什么状态,以及笔划是否符合规则。 如果是这样,我们可以继续下一个状态。 从代码中已经可以看到,每次我们偏离规则时,状态都应设置为failed

 override func touchesMoved(_ touches: Set, with event: UIEvent) { 
super.touchesMoved(touches, with: event)
let newTouch = touches.first
// There should be only the first touch.
guard newTouch == self.trackedTouch else {
self.state = .failed
return
}
let newPoint = (newTouch?.location(in: self.view))!
let previousPoint = (newTouch?.previousLocation(in: self.view))!
if self.strokePhase == .initialPoint {
// Make sure the initial movement is down and to the right.
if newPoint.x >= initialTouchPoint.x && newPoint.y >= initialTouchPoint.y {
self.strokePhase = .rightDownStroke
} else {
self.state = .failed
}
} else if self.strokePhase == .rightDownStroke {
// Make sure the initial movement is down and to the left.
if newPoint.y >= previousPoint.y {
if newPoint.x <= previousPoint.x {
self.strokePhase = .leftDownStroke
}
} else {
self.state = .failed
}
}
}
//Continue below
然后我们实现触摸结束时应该发生的情况。 我们验证最后一个笔划阶段是否为leftDownStroke ,并且最终点是否在初始点之下。 如果recogznied ,我们将状态设置为已recogznied ,如果未failed则将状态设置为已recogzniedoverride func touchesEnded(_ touches: Set, with event: UIEvent) {
super.touchesEnded(touches, with: event)
let newTouch = touches.first
// There should be only the first touch.
guard newTouch == self.trackedTouch else {
self.state = .failed
return
}
let newPoint = (newTouch?.location(in: self.view))!
// If the stroke was down up and the final point is
// below the initial point, the gesture succeeds.
if self.state == .possible &&
self.strokePhase == .leftDownStroke &&
newPoint.y > initialTouchPoint.y {
self.state = .recognized
} else {
self.state = .failed
}
}
//Continue below
最终,我们实现了重置功能,该功能将所有内容均设置为初始值,以便应用程序可以识别新手势。 在取消触摸的情况下,我们重置值并将状态设置为cancelledoverride func touchesCancelled(_ touches: Set, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
self.state = .cancelled
reset()
}
override func reset() {
super.reset()
self.initialTouchPoint = CGPoint.zero
self.strokePhase = .notStarted
self.trackedTouch = nil
}
} //End of class 2。将其添加到视图 view.addGestureRecognizer(CustomGestureRecognizer(target: self, action: #selector(openPhotoLibrary)))
 链接/资源 
  • 苹果—手势识别器状态机
  • 苹果—实施离散手势识别器
  • 苹果—实施连续手势识别器
  • 苹果— UITouch
 如果您正在寻找更多灵感,则可能需要查看其他自定义手势识别器教程: 
*
Apple *的Checkmark教程
raywenderlich.com的发痒教程*
raywenderlich.com的圈子教程