Objective-C:尝试在UIScrollView中实现我自己的拖动,遇到大量问题

时间:2011-06-09 17:16:50

标签: objective-c cocoa-touch ios

我正在尝试覆盖UITableView(实际上是UIScrollView子类)中的默认行为。基本上,我的表占据了屏幕的三分之一,我希望能够将项目从表格拖动到屏幕的其余部分 - 通过按住然后拖动,以及垂直拖动到表格。我能够使用默认的UIScrollView touchesShouldBegin / touchesShouldCancel和touchesBegan / Moved / Ended-Cancelled来实现第一种技术,但第二种技术给我带来了一些麻烦。

我的问题是:我希望能够检测到拖动,但我还希望能够在不拖动时滚动。为了做到这一点,我必须执行我的拖动检测,直到并包括调用touchesShouldCancel时的点。 (这是因为touchesShouldCancel是UIScrollView决定是否继续将触摸传递给其子视图或滚动的分支点。)不幸的是,UIScrollView的取消半径相当小,如果用户触摸一个单元格然后真的移动他们的手指很快,只接触到了开关。 (如果用户移动缓慢,我们通常会在调用touchesShouldCancel之前获得一些touchesMoved。)因此,我无法计算触摸方向,因为我只有touchesBegan中的一个点。

如果我可以在任何特定时刻查询触摸而不必依赖触摸回调,我可以很容易地解决这个问题,但据我所知,我不能这样做。如果我可以自行决定取消滚动视图(并随后调用touchesShouldCancel),或至少延迟调用touchesShouldCancel,也可以修复问题,但我也不能这样做。

相反,我一直试图通过UITableView在单独的叠加视图中捕获几个touchesBegan / Moved调用(最多2个或3个),然后将我的触摸转发到表。这样,我的表保证在调用touchesShouldCancel时已知道拖动方向。您可以在此处阅读有关此方法的变体:

(是的,他们做的事情有点不同,但我认为关键是在预处理完成后将触摸转发给UITableView。)

不幸的是,这似乎不起作用。使用touchesBegan / Moved / Ended-Cancelled调用我的表视图不会移动表,也不会将它们转发到表的hitTest视图(我的测试是表本身或表格单元)。更重要的是,我检查了单元格的nextResponder是什么,结果是表格,所以这也不起作用。根据我的理解,这是因为UIScrollView在近期的某个时刻切换到使用手势识别器来执行其重要的拖动/滚动检测,据我所知,你不能像通常的手势识别器那样转发触摸参与其中。

这是另一回事:即使手势识别器在3.2中正式发布,它们仍然在3.1.3中,尽管你不能使用API​​。我认为UIScrollView已经在3.1.3中使用它们了。

呼!所以这是我的问题:

  1. 上面两个链接中描述的nextResponder方法似乎很新。我做错了什么,或者从那时起UIScrollView的实现是否真的从根本上改变了?
  2. 有没有办法使用UIGestureRecognizers向类转发,确保识别器有机会处理触摸?
  3. 我可以通过添加自己的UIGestureRecognizer来检测问题,该UIGestureRecognizer检测拖动角度到我的表视图,然后确保在table.gestureRecognizers之前添加的每个手势识别器都取决于我的完成。 (我认为有3个默认的UIScrollView手势识别器。一些是私有API类,但显然都是UIGestureRecognizer子类。)我没有按名称处理任何私有手势识别器,但我仍在操纵它们还使用了我对UIScrollView内部的知识,这些内部结构没有被Apple记录。我的应用可以因此被拒绝吗?
  4. 我该怎么做3.1.3? UIScrollView显然已经使用了手势识别器,但我实际上无法访问它们,因为API仅在3.2中可用。
  5. 谢谢!

1 个答案:

答案 0 :(得分:0)

好的,我终于找到了问题的答案。实际上有两个答案。

Convoluted解决方案:子类UIWindow并覆盖sendEvent以存储最后一个触摸的位置。 (重写sendEvent是“事件处理指南”中给出的示例之一。)然后,Scroll View可以在调用touchesShouldCancel时查询窗口以获取最后一次触摸。

更简单的解决方案:不久之后,我注意到Facebook的Three20库存储UITouches而不保留它们。我一直认为你不应该将UITouch对象保留在本地范围之外,但Apple的文档只明确禁止保留。 (“UITouch对象在多点触控序列中是持久的。在处理事件时,不应该保留UITouch对象。如果需要保持从一个阶段到另一个阶段的触摸信息,您应该从UITouch对象复制该信息。“)因此,简单地将初始UITouch存储在表中并在调用touchesShouldCancel时查询其新位置可能是合法的。

不幸的是,在最坏的情况下,这两种技术都只给我2个采样点,这不是一个非常准确的方向测量。如果我可以简单地延迟表的触摸处理或手动调用touchesShouldCancel会好得多,但据我所知,要么是非常hacky或完全不可能/非法这样做。