UIPinchGestureRecognizer 与 UILongTapGestureRecognizer 的使用没有问题

Posted

技术标签:

【中文标题】UIPinchGestureRecognizer 与 UILongTapGestureRecognizer 的使用没有问题【英文标题】:Unproblematic use of UIPinchGestureRecognizer with UILongTapGestureRecognizer 【发布时间】:2018-03-09 09:40:34 【问题描述】:

我需要同时使用UILongTapGestureRecognizerUIPinchGestureRecognizer

不幸的是,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

【问题讨论】:

不是 100% 确定您在问什么...您是说您希望用户使用 3 个手指,其中一个用于长按手势,另一个用于捏合? 感谢您的帮助,是的,这正是我所需要的。 底线是,如果您使用UIGestureRecognizers,您将受到操作系统的支配——在本例中为UIKit可能有一种方法可以拦截第一次点击 - 但最好的办法是“深入”并仅使用触摸事件而不是手势。 (你会很高兴你这样做了。) 一些可能的批评?你为什么要求用户这样做(长按/点击然后捏)?大多数(几乎所有)应用程序都不会问他们的用户。您可能会看到其他应用程序如何处理您正在寻找的功能并复制它。无论哪种方式,使用触摸事件。祝你好运! 【参考方案1】:

我不相信你有你正在寻找的工具,所以我建议你尝试创建自己的手势识别器。这并不难,但不幸的是,您需要同时进行长按和捏合效果。

不要尝试覆盖 UIPinchGestureRecognizerUILongPressGestureRecognizer,因为它根本行不通(或者,如果你管理它,请分享你的发现)。所以直接去找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 长按成功时的计时器设置一个属性以指示长按确实触发了didSucceedLongPress = true 在开始时检查是否可以添加另一个触摸,如果不能添加则取消手势if longPressTouch != nil &amp;&amp; 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

然后,您需要检查一些边缘情况,例如: 用户开始长按,用两根手指捏合,添加第三根手指(忽略),移除第二根手指:如果不处理这个,你可能会得到一点跳跃,这可能是也可能不是有意的。我想您可以取消手势,或者您可以以某种方式转换初始值以使跳跃消失。

祝你好运,如果你要实施这个并告诉我们进展如何。

【讨论】:

令人印象深刻的答案!非常感谢你帮助我。我真的很想知道,你花了多长时间获得这些知识? @JohannaNoobie 基本上只是运气......我有一个手势识别器,它结合了长按和强制触摸,这就是第一部分。至于手动捏合和处理触摸,我有一些记忆,当时手势识别器还没有出现,我们不得不手动进行。

以上是关于UIPinchGestureRecognizer 与 UILongTapGestureRecognizer 的使用没有问题的主要内容,如果未能解决你的问题,请参考以下文章

UIPinchGestureRecognizer 与 UILongTapGestureRecognizer 的使用没有问题

使用 UIPinchGestureRecognizer 调整 UIView 的大小

UIPinchGestureRecognizer。放大手指的位置,而不仅仅是中心[重复]

UIPinchGestureRecognizer 向内捏“慢”

Swift - UIPageViewController中的UIPinchGestureRecognizer

当图像放大太大时,UIPinchGestureRecognizer 表现得很有趣