如何在 iOS 设备上的滑动手势期间“捕捉”子视图?

Posted

技术标签:

【中文标题】如何在 iOS 设备上的滑动手势期间“捕捉”子视图?【英文标题】:How can I "catch" a subview during a swipe gesture on an iOS device? 【发布时间】:2010-12-13 16:28:41 【问题描述】:

我正在构建一个 ios 棋盘游戏(类似于拼字游戏或文字游戏),其中涉及在屏幕上的小图块周围移动,我发现用户有时很难触摸和移动图块,因为它们太小的。由于游戏的设计,我无法增加图块的大小,所以我不得不实施一些小技巧,让用户更容易触摸和移动图块,而且效果很好。剩下的唯一问题是用户有时只触及磁贴之外,当用户尝试移动它时,磁贴会停留在原处。

对于如何解决这个问题,我有两个想法...

    如果用户触摸屏幕时没有触摸磁贴,我可以使用父视图的触摸位置来查找离触摸位置最近的磁贴,并以某种方式将触摸事件转发到该磁贴的视图。 如果在用户触摸屏幕时没有触摸磁贴,我可以在用户尝试移动时将手指拖过磁贴时以某种方式“捕捉”该磁贴。

我更喜欢实施解决方案 #2,因为解决方案 #1 有太多相关的问题,更不用说解决方案 #2 是更现实的体验。也就是说,当用户将手指拖到磁贴上并发送触摸事件以将其移动到手指所在的位置时,我如何“捕捉”磁贴?

目前,我的图块被实现为 UIView 的子类,它们直接处理触摸事件(touchesBegain、touchesMoved 和 touchesEnded)。因此,如果用户刚刚触摸到视图之外并将手指拖到视图上,它不会收到任何触摸事件,因为它没有收到初始的 touchesBegan 事件。

提前感谢您的智慧!

【问题讨论】:

【参考方案1】:

也许您应该让“板”视图处理所有拖动。当触摸开始并且在该点有一个图块时,然后开始拖动它。否则,在触摸移动时检查,一旦找到磁贴,就开始拖动它。

您可以在板视图中覆盖hitTest:withEvent:,以便它仍然可以检测触摸何时触及磁贴,但始终返回自身以便触摸事件进入板视图(例如,将被击中的子视图记录在单独的成员变量,以便您知道稍后在触摸事件开始时开始拖动什么)。

更多详情

在处理触摸时,UIView 将使用 hitTest 来查找应该接收触摸事件的视图。默认实现会检查每个子视图,以便层次结构中的“最深”子视图得到影响。为了让板视图接收触摸,您必须在所有平铺视图上禁用 userInteraction。但这意味着您不能使用 hitTest 来查找被触摸的图块,因为它会忽略禁用了 userInteraction 的视图。

所以我要说的是在触摸视图上启用 userInteraction。但是覆盖板视图上的 hitTest 以便它首先调用超级实现以查找图块(如果结果是self,则板本身被击中)。无需实现您自己的图块搜索。像这样的:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event

  UIView *hitView = [super hitTest:point withEvent:event];
  if ( hitView != self )
    self.draggingTile = hitView;
  return self;

现在您知道要在touchesMoved 中移动哪个图块了。但是,我认为在触摸移动时不会调用 hitTest,因此如果尚未拾取任何图块,您可能必须手动调用它(您可以从传递给的触摸中获取 pointevent touchesMoved.

【讨论】:

谢谢,布赖恩,这听起来是正确的做法。快速提问,如果我在板视图中覆盖 hitTest 方法,我到底想在其中做什么?我是否要循环遍历所有的瓷砖并检查生命值是否击中了任何瓷砖?如果是这样,那么重写 hitTest 方法有什么意义呢,我为什么不在板的触摸事件方法(即 touchesBegan、touchesMoved 等)中检查它? 更新了更多详细信息的答案 谢谢,布赖恩,这很有帮助。我会试一试!此外,由于我的板视图包含其他类型的视图而不仅仅是磁贴,我假设我必须检查从调用 [super hitTest] 返回的对象类型并确保它是磁贴视图。我应该为此使用 [hitView isKindOfClass:[TileView class]] 吗?【参考方案2】:

您查看过UIGestureRecognizer API 吗?我认为您最好的选择是将 UIPanGestureRecognizer 识别器添加到您的板子视图中,然后将选择器回火到您的 UIViewController。

ViewDidLoad 中的设置:

UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
                                                      initWithTarget:self action:@selector(handlePan:)];

[[self view] addGestureRecognizer:panGestureRecognizer];

然后实现选择器:

    - (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
          CGPoint currentPoint = [gesture locationInView:[self view]];
    

当您设置手势识别器时,您可以设置参数以限制回调(仅识别带有 1 根手指的平移等。在回调中您可以检查手势属性以查看平移是否继续或是否即将到来结束。您还可以抓取当前点并确定它是否在图块中或附近。

【讨论】:

谢谢,西萨里斯劳。问题,您的解决方案的重点似乎是 1)检测平移手势和 2)获取它的当前位置,这样我就可以用它做任何我想做的事情,对吗?如果没有,我很抱歉,我对 iOS 还是比较陌生。如果是这样,为什么不直接使用 touchesMoved 事件方法来获取这些信息呢?再次感谢! 我发现手势提供了对反馈的更多控制,并为我做了很多低级工作,让我可以专注于应用程序逻辑而不是触摸检测(这是点击还是平移?等) .) 我的应用程序是一个绘图应用程序,它需要验证用户是否以特定顺序从一个区域平移到另一个区域。手势是 iOS4 的新功能,因此如果您需要 3.2 或更早版本的兼容性,触摸检测是您唯一的选择。 另外我依稀记得遇到了一些问题,触摸事件有时会被其他视图拦截而不传递(我想说的是 UIScrollView 对此特别糟糕)。

以上是关于如何在 iOS 设备上的滑动手势期间“捕捉”子视图?的主要内容,如果未能解决你的问题,请参考以下文章

识别视图中的滑动手势而不是子视图

如何阻止从超级视图到子视图的手势?

跨多个子视图的 SwiftUI 拖动手势

Android ViewFlipper + 手势检测器

UITableView滑动删除iOS上的手势冲突

如何在 ios 11 中使用主详细信息视图禁用向后滑动手势?