iOS:检测触地、segue、触地

Posted

技术标签:

【中文标题】iOS:检测触地、segue、触地【英文标题】:iOS: Detecting touch down, segue, touch up 【发布时间】:2014-03-16 06:51:40 【问题描述】:
    用户将手指放在屏幕上。这会触发UITouchEvent,阶段Began,它调用controllerA 中的touchesBegan:withEvent: 方法,执行从controllerAcontrollerB 的segue。 用户将手指从屏幕上移开。这会触发一个UITouchEvent,阶段Ended,它会调用一些回调方法。

问题:这个回调方法是什么,在哪里?它不在controllerA 中,也不在controllerB 中。据我所知,这不是任何观点。但它存在。

【问题讨论】:

你不能在 VC A 中为触摸发布做一个 NSLOG 吗?很确定这就是所谓的那个。 不,正如我已经提到的,在任何一个控制器中都没有调用它。 你想做什么?在执行 segue 时将参数从火传递到另一个? 添加了说明。 有什么想法吗?这似乎是一个非常普遍的问题。 【参考方案1】:

为了澄清,这是发生了什么(根据@switz):

响应-touchesBegan:withEvent:,呈现一个视图控制器 模态地通过segue 当用户抬起手指时,应关闭视图控制器。

问题是如何对抬起的手指做出反应,因为 -touchesEnded:withEvent: 未被调用。

简短的回答是呈现的视图控制器需要使用“Over Full 屏幕”modalPresentationStyle 而不是默认的“全屏”样式 (这可以指定为 segue 的表示样式,或者如果 那是“默认”,然后是呈现的视图控制器的呈现风格)。

长答案需要简要概述触摸处理的工作原理。这 解释忽略了手势识别器:

当触摸开始时,它被传递到包含 接触点。从那里它沿着响应者链传递,直到一些 对象决定处理触摸(通过实现 -touchesBegan:withEvent: 而不是调用 super)。

对触摸的后续更改(例如,移动、结束、取消)会返回 到接受触摸的相同视图。该视图将继续收到 触摸事件,直到触摸完成或取消。

当应用程序移动到后台时,触摸被取消(因为 例如有电话打进来),或者当像 UIScrollView 这样的 UIKit 类决定时 它需要接管触摸处理(因为手指移动得足够远 看起来用户想要滚动)。这里还有一些有趣的东西 UIScrollView.delaysContentTouches,但可以忽略。

但是有一个问题,没有记录在案:仅触摸交付 只要视图保持与窗口相关联,就会发生。如果认为 被认为是“最顶层”(与UITouch 关联的视图)是 从窗口中移除,则认为触摸已消失,并且, 重要的是,该触摸的任何事件都不会再次传递给任何人。这是 即使有问题的视图不是对象处理触摸也是如此。

最后的皱纹是造成这个问题的原因。因为默认 “全屏”演示样式实际上删除旧视图控制器的 从窗口查看,触摸处理立即停止。然而,“过 全屏”演示风格并没有删除它,它只是掩盖了旧的 查看与一个。 “Over Full Screen”通常在呈现视图时使用 控制器不是完全不透明的,但在这种情况下,我们正在使用它,所以触摸 处理不会中断。


但这还不是全部。这里还有一个问题,就是当视图 被触摸住在 UIScrollView 内(一个可以滚动或 总是反弹)。在这种情况下,即使使用“全屏”,您也会发现, 在继续传递触摸事件的同时,稍微移动手指 突然导致触摸被取消。这是因为UIScrollView 不知道它被掩盖了,并决定用户实际上是在尝试 滚动。这会导致它取消触摸。

不过,有一个解决方案。这有点难看,但解决方案是 执行时立即取消任何封闭滚动视图上的任何滚动 转场。这可以通过如下代码来完成:

class ViewController: UIViewController 
    // this is called from -touchesBegan:withEvent: from a child view
    // the child view is `sender`
    func touchDown(sender: UIView) 
        var view = sender.superview
        while view != nil 
            if let scrollView = view as? UIScrollView 
                // toggle the panGestureRecognizer enabled state to immediately
                // cause it to fail.
                let enabled = scrollView.panGestureRecognizer.enabled
                scrollView.panGestureRecognizer.enabled = true
                scrollView.panGestureRecognizer.enabled = enabled
            
            view = view?.superview
        
        performSegueWithIdentifier(identifier, sender: self)
    
    // ...


当然,没有手势就没有关于触摸处理的讨论是完整的 识别器。手势识别器几乎改变了触摸的一切 处理。他们在任何触摸时都会获得第一手,并且可以中断视图触摸 随时处理。例如,UIScrollViewUIPanGestureRecognizer 是用于滚动的,当它进入“开始”状态时(因为 用户已经移动了足够多的手指),这就是导致触摸的原因 取消。

因此,考虑到这一点,最好的解决方案是不实施 -touchesBegan:withEvent: 完全没有,但要使用手势识别器。最简单的 这里的解决方案是使用UILongPressGestureRecognizer minimumPressDuration 设置为 0allowableMovement 设置为一些 高得离谱的价值(因为你不想移动来取消触摸)。我是 推荐这个是因为UILongPressGestureRecognizer 是一个连续的 识别器,这意味着它将发送 Began、Moved 和 Ended 的事件,并带有 推荐的设置,它将响应触摸开始发送它们, 移动,结束。更重要的是,一旦你的识别器开始处理触摸, 这会自动阻止任何其他识别器(例如滚动视图的平移 识别器)从“接管”和取消触摸。


请注意,如果您将手势识别器附加到 scrollView 本身 (例如UITableView)但只想响应某些位置的触摸 (例如连续),那么您需要限制识别器。您可以使用 委托方法gestureRecognizer(_:shouldReceiveTouch:) 来做这件事 像这样:

func gestureRecognizer(recognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool 
    // if you might be the delegate of multiple recognizers, check for that
    // here. This code will assume `recognizer` is the correct recognizer.
    // We're also assuming, for the purposes of this code, that we're a
    // UITableViewController and want to only capture touches on rows in the
    // first section.
    let touchLocation = touch.locationInView(self.tableView)
    if let indexPath = self.tableView.indexPathForRowAtPoint(touchLocation) 
        if indexPath.section == 0 
            // we're on one of the special rows
            return true
        
    
    return false

这样识别器就不会阻止tableViewpanGestureRecognizer 当用户触摸桌子上的其他地方时不会滚动。

【讨论】:

作为一个仅供参考 - 我将长按最短持续时间修改为 0.1 并检查了 if (recognizer != yourLongPressGestureRecognizer) return true 这使滚动识别器优先于长按。这使用户可以相当轻松地滚动,同时仍然允许无缝“按住”。此外,如果您有一个附件按钮,您可以检测您的按键是否在最右侧。 if fingerLocation.x > self.tableView.bounds.size.width - 40 // accessory tap

以上是关于iOS:检测触地、segue、触地的主要内容,如果未能解决你的问题,请参考以下文章

在按钮 B 的触地期间触发按钮 A 上的触地

Sprite Kit 接触检测

以一种奇怪的方式触地射击

触地重复事件不起作用

Swift 动画 - 触地得分效果不佳

Unity300个技巧检测物体在地面的三种方式