自定义手势,一般继承自某一类系统手势。
本文的例子是,自定制右滑,仅仅右滑,方向夹角,限制在上下 20 度。必须移动 30 个像素,才触发。
手势右滑,简单一些,就是看速度
@objc func push(_ gesture: UIPanGestureRecognizer){
if gesture.state == UIGestureRecognizer.State.began{
let velocity = gesture.velocity(in: gesture.view)
if velocity.x>0{
// ...
}
}
}
要添加更多的限制,最好自定制手势
继承手势,能操作的就是几个 touches
方法,
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent)
等 3 个
继承手势并自定制的关键,是状态管理。
一般手势触发的状态,有 Began
和 Ended
, Posssible
和 Cancle
不触发。
自定制手势的内部状态一 Ended
, 外部的方法,就会触发。
直接重写,状态全部自己来管理。
如果调用父类的实现,super.touches
, 状态就会多一些系统的实现。
例如,仅仅在
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
}
state 会从 possible , 到 began, 再到 changed
可以通过 state 的 rawValue 来查看
剩下的,就是条件判断了。
有了什么样的条件,就会决定什么样的状态。
下面的代码,是通过判断坐标,方便做容错。
以免用户手指想点击,操作成平移,把界面移没了。
一般情况下,可以用速度,不用坐标
源代码:
struct Constaint {
let maxAngle: Double
let minSpeed: CGFloat
let toleranceDistance:CGFloat
static let `default` = Constaint(maxAngle: 20, minSpeed: 40, toleranceDistance: 30)
}
class RightPanGesture: UIPanGestureRecognizer {
var curTickleStart: CGPoint?
let constraint: Constaint
init(target: AnyObject, action: Selector, constraint limits: Constaint = Constaint.default) {
constraint = limits
super.init(target: target, action: action)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if let touch = touches.first {
curTickleStart = touch.location(in: view)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if let start = curTickleStart, let touch = touches.first{
let ticklePoint = touch.location(in: view)
let moveAmt = ticklePoint.x - start.x
guard moveAmt > constraint.toleranceDistance else{
return
}
let tangent = tan(constraint.maxAngle * Double.pi / 180)
if abs(ticklePoint.y - start.y)/abs(moveAmt) > CGFloat(tangent){
state = .cancelled
}
if state == .possible{
state = .ended
}
}
}
override func reset() {
curTickleStart = nil
if state == .possible {
state = .failed
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
reset()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
reset()
}
}
调用:
class SidePanView: UIView{
override init(frame: CGRect) {
super.init(frame: frame)
let pan = RightPanGesture(target: self, action: #selector(SidePanView.push(_:)))
addGestureRecognizer(pan)
isUserInteractionEnabled = true
}
@objc func push(_ gesture: RightPanGesture){
// ...
}
}