可拖动的 UIView 在添加到 UIScrollView 后停止发布 touchesBegan
Posted
技术标签:
【中文标题】可拖动的 UIView 在添加到 UIScrollView 后停止发布 touchesBegan【英文标题】:Draggable UIView stops posting touchesBegan after being added to UIScrollView 【发布时间】:2014-03-16 17:33:09 【问题描述】:在 Xcode 5.1 中,我为 iPhone 创建了a simple test app:
结构是:scrollView -> contentView -> imageView -> image 1000 x 1000
在上面。
在单视图应用程序的底部,我有七个可拖动的自定义UIView
s。
在Tile.m 中使用touchesXXXX
方法实现拖动。
我的问题是:一旦我在ViewController.m 文件中的contentView
中添加了一个可拖动的磁贴 - 我就不能再拖动它了:
- (void) handleTileMoved:(NSNotification*)notification
Tile* tile = (Tile*)notification.object;
//return;
if (tile.superview != _scrollView && CGRectIntersectsRect(tile.frame, _scrollView.frame))
[tile removeFromSuperview];
[_contentView addSubview:tile];
[_contentView bringSubviewToFront:tile];
Tile
不再调用 touchesBegan
,就好像 scrollView
会掩盖该事件一样。
I've searched around 并且有人建议使用以下方法扩展 UIScrollView
类(在我的自定义 GameBoard.m 中):
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
UIView* result = [super hitTest:point withEvent:event];
NSLog(@"%s: %hhd", __PRETTY_FUNCTION__,
[result.superview isKindOfClass:[Tile class]]);
self.scrollEnabled = ![result.superview isKindOfClass:[Tile class]];
return result;
不幸的是,这并没有帮助并在调试器中打印0
。
【问题讨论】:
只是为了更新,UIScrollView 不响应触摸事件...我会检查你的代码... 滚动视图当然可以包含可拖动的视图,我的这个示例代码证明了这一点(您可以在自己的机器上下载并尝试):github.com/mattneub/Programming-ios-Book-Examples/blob/master/… 并在我的书中的这一部分查看该项目的讨论:apeth.com/iOSBook/ch20.html#_scroll_view_touches - 基本上我会说从我正在做的事情开始(有效)然后调整它。无论如何,您都应该使用手势识别器,而不是直接触摸事件。 @AlexanderFarber 我发现了问题。您的内容视图已启用用户交互 = 关闭。打开它后,我可以拖动瓷砖。我现在明白为什么它关闭了。我会在一分钟内给你一个解决方案 @AlexanderFarber Aaand 完成。 :D 【参考方案1】:问题的部分原因在于内容视图上的用户交互被禁用。但是,启用用户交互会禁用滚动,因为视图会捕获所有触摸。所以这是解决方案。在故事板中启用用户交互,但像这样子类化内容视图:
@interface LNContentView : UIView
@end
@implementation LNContentView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
UIView* result = [super hitTest:point withEvent:event];
return result == self ? nil : result;
@end
这样,只有当接受视图不是self
,即内容视图时,命中测试才会通过。
这是我的承诺: https://github.com/LeoNatan/ios-newbie
【讨论】:
创建了一个拉取请求。 +1 太好了,谢谢。我想(并且必须 - 根据 *** 规则)等待几天的赏金 - 以防有更多答案。我还将等待 GitHub 拉取请求 - 这样其他 *** 用户就不会感到困惑(因为代码中不存在问题)。 @AlexanderFarber 当然,好的。如果您还需要什么,请告诉我。 @AlexanderFarber 您需要使用-convertPoint:toView:
将原点转换为滚动视图空间。不确定这是否考虑了缩放比例。如果没有,您可能需要赔偿。
为什么底部托盘不是滚动视图层次结构的一部分?这样你就可以把它带到前面,移动,然后在释放时看看瓷砖是否在托盘上并将它放在那里。【参考方案2】:
平铺视图没有被触摸的原因是滚动视图的平移手势识别器消耗了这些事件。您需要的是,将UIPanGestureRecongnizer
附加到您的每个图块并进行如下配置:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; // handle drag in pan:method
[tile addGestureRecognizer:pan];
UIPanGestureRecognizer *scrollPan = self.scrollView.panGestureRecognizer;
[scrollPan requireGestureRecognizerToFail:pan];
在这里,您让滚动视图的平移手势识别器知道您只希望在没有任何图块被拖动时发生滚动。
我已经检查过这个方法——它确实有效。关于您的代码,您需要在手势识别器而不是平铺视图中处理所有触摸,因为触摸事件可能会在经过命中测试的视图的手势识别器到达视图本身之前被消耗/延迟。请参阅UIGestureRecognizer documentation 了解有关该主题的更多信息。
【讨论】:
+1 谢谢,Leonty,但是如果我从touchesBegan
切换到手势识别器,是否仍然可以在用户触摸时显示 Tile 阴影(Tile.xib
中的 bigImage
) ?
@AlexanderFarber 您可以创建手势识别器的子类并在其touchesBegan:withEvent:
中定义您需要的任何行为。我会将实际处理委托给视图控制器或 Tile 视图本身。现在,您可以从自定义平移手势识别器中调用视图上的相应方法。
@AlexanderFarber 哦,对不起!我没有注意到真正的混乱是在界面生成器中! ? 阅读了上面与 Leo 的讨论后,我可以说该解决方案比任何提议的解决方案都更容易。 ? 如果您在 IB 中将滚动视图上的内容 delaysContentTouches
设置为 YES 并在内容视图上启用用户交互,您将获得预期的行为(以触摸传递延迟为模)。
……虽然你不一定想要延迟。所以+1对Leo的回答。 :)【参考方案3】:
看起来层次结构中的一个视图正在捕获事件。
看看这个部分
响应者链遵循特定的传递路径
Apple 文档的here
编辑:
对不起,我是凭记忆写的。这就是我在自己的应用中解决类似问题的方法:
我在要检测触摸的视图中使用UITapGestureRecognizer
。实现 UITapGestureRecognizer 的以下委托方法:
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
touches 集合包含所有接收到事件的对象(视图)。
【讨论】:
如何过滤此类事件? 您也可以强制视图不重叠。 抱歉,但我不明白强制视图不重叠如何帮助我解决问题。另外,我不能使用点击手势识别器:我必须使用touchesBegan
,因为当用户触摸瓷砖时,我会显示带有阴影的图像(bigImage
)。以上是关于可拖动的 UIView 在添加到 UIScrollView 后停止发布 touchesBegan的主要内容,如果未能解决你的问题,请参考以下文章