iOS:检测触地、segue、触地
Posted
技术标签:
【中文标题】iOS:检测触地、segue、触地【英文标题】:iOS: Detecting touch down, segue, touch up 【发布时间】:2014-03-16 06:51:40 【问题描述】:-
用户将手指放在屏幕上。这会触发
UITouchEvent
,阶段Began
,它调用controllerA
中的touchesBegan:withEvent:
方法,执行从controllerA
到controllerB
的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)
// ...
当然,没有手势就没有关于触摸处理的讨论是完整的
识别器。手势识别器几乎改变了触摸的一切
处理。他们在任何触摸时都会获得第一手,并且可以中断视图触摸
随时处理。例如,UIScrollView
的 UIPanGestureRecognizer
是用于滚动的,当它进入“开始”状态时(因为
用户已经移动了足够多的手指),这就是导致触摸的原因
取消。
因此,考虑到这一点,最好的解决方案是不实施
-touchesBegan:withEvent:
完全没有,但要使用手势识别器。最简单的
这里的解决方案是使用UILongPressGestureRecognizer
minimumPressDuration
设置为 0
和 allowableMovement
设置为一些
高得离谱的价值(因为你不想移动来取消触摸)。我是
推荐这个是因为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
这样识别器就不会阻止tableView
的panGestureRecognizer
当用户触摸桌子上的其他地方时不会滚动。
【讨论】:
作为一个仅供参考 - 我将长按最短持续时间修改为0.1
并检查了 if (recognizer != yourLongPressGestureRecognizer) return true
这使滚动识别器优先于长按。这使用户可以相当轻松地滚动,同时仍然允许无缝“按住”。此外,如果您有一个附件按钮,您可以检测您的按键是否在最右侧。 if fingerLocation.x > self.tableView.bounds.size.width - 40 // accessory tap
以上是关于iOS:检测触地、segue、触地的主要内容,如果未能解决你的问题,请参考以下文章