在 UIScrollView 中滚动而不触发 touchesCancelled

Posted

技术标签:

【中文标题】在 UIScrollView 中滚动而不触发 touchesCancelled【英文标题】:Scrolling in a UIScrollView without triggering touchesCancelled 【发布时间】:2011-03-10 15:02:00 【问题描述】:

概述

我正在开发一款 iPhone 游戏,其代码是从另一位开发者那里继承而来的。游戏网格是一个 UIScrollView,其 contentSize 为 1000x1000。网格包含通过 OpenGL 的 Texture2D 类在屏幕上绘制的多个元素。

UIScrollView 的滚动方向与您手指移动的方向相反。这背后的想法是模拟用手指“筛选”元素的行为,因为触摸路径中的每个元素都应该受到影响/操纵。

 

问题

问题在于 UIScrollView 的滚动触发了 touchesCancelled,从而结束了游戏依赖的 touchesMoved 调用来知道要操作哪些元素。即使我的手指移动不会在视觉上触发视图滚动,将手指移动超过 10 个像素的距离也会触发 touchesCancelled 方法,从而结束未来的 touchesMoved 调用,直到我抬起手指并开始新的触摸事件。

我确信触发 touchesCancelled 方法的是滚动事件,因为如果我在视图上设置 self.scrollingEnabled = NO;,则在屏幕上移动手指会继续触发 touchesMoved,无论我移动多远手指在屏幕上,当我抬起手指时(在完成对元素的筛选之后),touchesEnded 会按预期被调用,并且我的触摸/滑动路径中的每个元素确实都按需要进行了操作。但是,设置此属性自然会阻止游戏网格的所需滚动。

 

尝试修复失败

我尝试在视图的initWithFrame 方法中设置self.canCancelContentTouches = NO;,但touchesCancelled 仍然被触发,这很奇怪。不知道为什么会这样!也许我把它放在了错误的位置,但我不太确定这是否能解决我的问题,因为文档暗示这样的设置无论如何都会阻止滚动:“如果这个属性的值否,一旦内容视图开始跟踪,无论手指移动如何,滚动视图都不会滚动。” 显然这不是我所追求的行为,因此该属性似乎不是我感兴趣的毕竟。虽然我仍然对为什么它仍然滚动并调用touchesCancelled 感到困惑,但我离题了。

我还添加了 - (BOOL)touchesShouldCancelInContentView:(UIView *)view 方法,但它没有被调用,即使 没有 设置 self.canCancelContentTouches = NO; 因为文档说将 canCancelTouches 属性设置为 NO 确实会阻止 touchesShouldCancelInContentView 方法被调用:“如果 canCancelContentTouches 属性的值为 NO,则滚动视图不会调用此方法。”。尽管文档似乎不清楚从该方法返回 NO 是否也会像 canCancelContentTouches = NO; 设置那样阻止滚动。如果不是,那么这将是理想的解决方案。不过,我不知道为什么这个方法甚至没有被调用,因为文档只提到了一个阻止它被调用的属性/设置,即使该属性没有设置为 NO,它仍然没有被调用(因此默认为 YES)。

 

我该往哪里走?

所以现在我很茫然。事实上,在视图开始滚动后,我仍然需要 touchesMoved 才能继续被调用。这有可能吗?如果没有,我有什么选择?

【问题讨论】:

嗯...只是一个想法。为什么不能控制滚动,因为据我了解,您可以根据手指在屏幕中心的位置滚动视图.. 听起来是个有趣的方法,但我该怎么做呢?我在 UIScrollView 文档中看不到任何似乎指向能够像这样手动处理滚动的方法。 【参考方案1】:

我遇到了完全相同的问题(并且浪费了很多时间)。 然后我尝试尽快设置cancelsTouchesInView 属性(我在viewDidLoad 中完成了它)......它对我有用(as shown here)!

- (void)viewDidLoad 
    [super viewDidLoad];
    /* other initializations */
    scrollView.panGestureRecognizer.cancelsTouchesInView = NO;

这适用于ios 5。在iOS 5之前你需要遍历所有识别器,找到panGestureRecognizer,然后设置属性。 所以这应该是一个通用的解决方案:

- (void)viewDidLoad 
    [super viewDidLoad];
    /* other initializations */
    for (UIGestureRecognizer *gesture in scrollView.gestureRecognizers)
        if ([gesture isKindOfClass:[UIPanGestureRecognizer class]])
        gesture.cancelsTouchesInView = NO;    
    

【讨论】:

我在我的UIScrollView 视图子类中使用了这种方法。它在-initWithFrame: 中的效果与在-viewDidLoad 中一样好。我还使用了运行时测试来查看[self respondsToSelector:@selector(panGestureRecognizer)] 是否在实现之间切换。【参考方案2】:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated 

这可能会解决问题....任何 touchesMoved 都可以触发一个方法,该方法将以与屏幕中心(或其他任何地方)和最后记录的触摸位置之间的距离成比例的速度滚动,直到 touchesEnded。

【讨论】:

我不认为这种方法是你想要的。它只是将指定的 CGRect 滚动到视图中。它与触摸无关。我不认为这对我有用。

以上是关于在 UIScrollView 中滚动而不触发 touchesCancelled的主要内容,如果未能解决你的问题,请参考以下文章

允许 UIScrollView 滚动过去的内容而不弹跳

阻止UIScrollView中的UISlider触发滚动

在 UIScrollView 中延迟加载不同尺寸的图像而不分页

UIWebView 的 UIScrollView 子视图可以滚动但会弹回框架而不出现垂直滚动指示器

Swift:以编程方式添加视图时,UIScrollView 的可滚动内容大小不明确,而不使用 IB

为啥 UIScrollView 的滚动会在 iOS 4.3 中触发 layoutSubViews 方法,而在 5.0 及以上版本中却没有?