UIPinchGestureRecognizer 与 UILongTapGestureRecognizer 的无问题使用
Unproblematic use of UIPinchGestureRecognizer with UILongTapGestureRecognizer
我需要同时使用 UILongTapGestureRecognizer
和 UIPinchGestureRecognizer
。
不幸的是,UILongTapGestureRecognizer
的第一次触摸也将计入 PinchGestureRecognizer
。因此,当按住 UILongTapGestureRecognizer
时,只需再触摸一次即可触发捏合识别器。一个用于长按手势,两个用于捏合。
有没有办法将两者独立使用?我不想为我的 UIPinchGestureRecognizer
.
使用 UILongTapGestureRecognizer
的触摸
这就是我启用同步劳动力的方式:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
//Allowing
if (gestureRecognizer == zoom) && (otherGestureRecognizer == longTap) {
print("working while filming")
return true
}
return false
}
我认为您没有所需的工具,因此我建议您尝试创建自己的手势识别器。这并不是真的那么难,但不幸的是,您需要同时执行长按和捏合效果。
不要尝试覆盖 UIPinchGestureRecognizer
或 UILongPressGestureRecognizer
,因为它根本行不通(或者如果您成功了,请分享您的发现)。所以直接去 UIGestureRecognizer
.
因此,要开始使用长按手势识别器,我们需要跟踪用户在没有移动太多的情况下按下并按住足够长的时间。所以我们有:
var minimumPressDuration = UILongPressGestureRecognizer().minimumPressDuration
var allowableMovement = UILongPressGestureRecognizer().allowableMovement
现在需要覆盖触摸(这都在手势识别器的子类中):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
touchMoveDistance = 0.0 // Reset the movement to zero
previousLocation = touches.first?.location(in: view) // We need to save the previous location
longPressTimer = Timer.scheduledTimer(timeInterval: minimumPressDuration, target: self, selector: #selector(onTimer), userInfo: nil, repeats: false) // Initiate a none-repeating timer
if inProgress == false { // inProgress will return true when stati is either .began or .changed
super.touchesBegan(touches, with: event)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if let newLocation = touches.first?.location(in: view), let previousLocation = previousLocation {
self.previousLocation = newLocation
touchMoveDistance += abs(previousLocation.y-newLocation.y) + abs(previousLocation.x-newLocation.x) // Don't worry about precision of this. We can't know the path between the 2 points anyway
}
if inProgress == false {
super.touchesMoved(touches, with: event)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
longPressTimer?.invalidate()
longPressTimer = nil
if inProgress {
state = .ended
}
super.touchesEnded(touches, with: event)
if self.isEnabled { // This will simply reset the gesture
self.isEnabled = false
self.isEnabled = true
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
longPressTimer?.invalidate()
longPressTimer = nil
if inProgress {
state = .ended
}
super.touchesCancelled(touches, with: event)
if self.isEnabled {
self.isEnabled = false
self.isEnabled = true
}
}
所以这些都是长按。计时器上发生的事情是:
@objc private func onTimer() {
longPressTimer?.invalidate()
longPressTimer = nil
if state == .possible {
state = .began
}
}
所以基本上,如果我们将状态更改为 .begin
,我们就会触发手势,其余事件就会正常工作。这很整洁。
不幸的是,这还远未结束,您将需要对其余代码进行一些尝试...
您将需要保留触摸(如果我没记错的话,相同的触摸将被报告为可比较的对象,直到用户抬起手指):
- 开始时将触摸保存到私人 属性
longPressTouch
- On timer when long press successs set a 属性 表示长按确实触发了
didSucceedLongPress = true
- 开始检查是否可以添加另一个触摸,如果不能则取消手势
if longPressTouch != nil && didSucceedLongPress == false { cancel() }
。还是允许吧,这真的取决于你想要什么。
- 开始时,如果可以添加触摸,则将它们添加到数组中并保存它们的初始位置
pinchTouches.append((touch: touch, initialPosition: touchPosition))
- 在触摸结束和取消时确保从数组中删除适当的触摸。如果取消长按取消事件(或不取消,再次选择)
所以这应该是您的捏合手势识别器所需的所有数据。由于所有事件都应该已经按照您需要的方式为您触发,您所需要的只是您的比例的计算值:
var pinchScale: CGFloat {
guard didSucceedLongPress, pinchTouches.count >= 2 else {
return 1.0 // Not having enough data yet
}
return distanceBetween(pinchTouches[0].touch, pinchTouches[1].touch)/distanceBetween(pinchTouches[0].initialPosition, pinchTouches[1].initialPosition) // Shouldn't be too hard to make
}
然后您需要检查一些边缘情况,例如:
用户启动长按,使用 2 根手指捏合,添加第 3 根手指(忽略),移开第 2 根手指:如果不处理这个,您可能会有点跳跃,这可能是有意的,也可能不是有意的。我猜你可以取消手势,或者你可以以某种方式改变初始值以使跳跃消失。
祝你好运,如果你将实施这个并让我们知道它是如何进行的。
我需要同时使用 UILongTapGestureRecognizer
和 UIPinchGestureRecognizer
。
不幸的是,UILongTapGestureRecognizer
的第一次触摸也将计入 PinchGestureRecognizer
。因此,当按住 UILongTapGestureRecognizer
时,只需再触摸一次即可触发捏合识别器。一个用于长按手势,两个用于捏合。
有没有办法将两者独立使用?我不想为我的 UIPinchGestureRecognizer
.
UILongTapGestureRecognizer
的触摸
这就是我启用同步劳动力的方式:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
//Allowing
if (gestureRecognizer == zoom) && (otherGestureRecognizer == longTap) {
print("working while filming")
return true
}
return false
}
我认为您没有所需的工具,因此我建议您尝试创建自己的手势识别器。这并不是真的那么难,但不幸的是,您需要同时执行长按和捏合效果。
不要尝试覆盖 UIPinchGestureRecognizer
或 UILongPressGestureRecognizer
,因为它根本行不通(或者如果您成功了,请分享您的发现)。所以直接去 UIGestureRecognizer
.
因此,要开始使用长按手势识别器,我们需要跟踪用户在没有移动太多的情况下按下并按住足够长的时间。所以我们有:
var minimumPressDuration = UILongPressGestureRecognizer().minimumPressDuration
var allowableMovement = UILongPressGestureRecognizer().allowableMovement
现在需要覆盖触摸(这都在手势识别器的子类中):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
touchMoveDistance = 0.0 // Reset the movement to zero
previousLocation = touches.first?.location(in: view) // We need to save the previous location
longPressTimer = Timer.scheduledTimer(timeInterval: minimumPressDuration, target: self, selector: #selector(onTimer), userInfo: nil, repeats: false) // Initiate a none-repeating timer
if inProgress == false { // inProgress will return true when stati is either .began or .changed
super.touchesBegan(touches, with: event)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if let newLocation = touches.first?.location(in: view), let previousLocation = previousLocation {
self.previousLocation = newLocation
touchMoveDistance += abs(previousLocation.y-newLocation.y) + abs(previousLocation.x-newLocation.x) // Don't worry about precision of this. We can't know the path between the 2 points anyway
}
if inProgress == false {
super.touchesMoved(touches, with: event)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
longPressTimer?.invalidate()
longPressTimer = nil
if inProgress {
state = .ended
}
super.touchesEnded(touches, with: event)
if self.isEnabled { // This will simply reset the gesture
self.isEnabled = false
self.isEnabled = true
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
longPressTimer?.invalidate()
longPressTimer = nil
if inProgress {
state = .ended
}
super.touchesCancelled(touches, with: event)
if self.isEnabled {
self.isEnabled = false
self.isEnabled = true
}
}
所以这些都是长按。计时器上发生的事情是:
@objc private func onTimer() {
longPressTimer?.invalidate()
longPressTimer = nil
if state == .possible {
state = .began
}
}
所以基本上,如果我们将状态更改为 .begin
,我们就会触发手势,其余事件就会正常工作。这很整洁。
不幸的是,这还远未结束,您将需要对其余代码进行一些尝试...
您将需要保留触摸(如果我没记错的话,相同的触摸将被报告为可比较的对象,直到用户抬起手指):
- 开始时将触摸保存到私人 属性
longPressTouch
- On timer when long press successs set a 属性 表示长按确实触发了
didSucceedLongPress = true
- 开始检查是否可以添加另一个触摸,如果不能则取消手势
if longPressTouch != nil && didSucceedLongPress == false { cancel() }
。还是允许吧,这真的取决于你想要什么。 - 开始时,如果可以添加触摸,则将它们添加到数组中并保存它们的初始位置
pinchTouches.append((touch: touch, initialPosition: touchPosition))
- 在触摸结束和取消时确保从数组中删除适当的触摸。如果取消长按取消事件(或不取消,再次选择)
所以这应该是您的捏合手势识别器所需的所有数据。由于所有事件都应该已经按照您需要的方式为您触发,您所需要的只是您的比例的计算值:
var pinchScale: CGFloat {
guard didSucceedLongPress, pinchTouches.count >= 2 else {
return 1.0 // Not having enough data yet
}
return distanceBetween(pinchTouches[0].touch, pinchTouches[1].touch)/distanceBetween(pinchTouches[0].initialPosition, pinchTouches[1].initialPosition) // Shouldn't be too hard to make
}
然后您需要检查一些边缘情况,例如: 用户启动长按,使用 2 根手指捏合,添加第 3 根手指(忽略),移开第 2 根手指:如果不处理这个,您可能会有点跳跃,这可能是有意的,也可能不是有意的。我猜你可以取消手势,或者你可以以某种方式改变初始值以使跳跃消失。
祝你好运,如果你将实施这个并让我们知道它是如何进行的。