UIScrollView将触摸事件发送给子视图

12

注意: 我已经阅读了一些关于UIScrollView向子视图发送触摸事件的问题(包括这个问题),尽管我已经表示赞成,但它不再按照我预期的工作。

我的问题: 我有一个包含自定义UIView(我们称之为A)的UIScrollView,它覆盖整个UIScrollView。我还可以在A中放置其他自定义UIViews

在代码中,我正在做如下操作:

[scrollView setDelaysContentTouches:YES];
scrollView.canCancelContentTouches = NO;

当前情况:目前我的唯一问题是,如果我想在A中移动一个子视图,我必须触摸它,等待一段时间,然后才能移动它。就像这里所述的那样:

现在,行为取决于对UIView的第一次触摸的“持续时间”。如果很短,则相对拖动将被处理为UIScrollView的滚动。如果持续时间较长,则我将在我的UIView中获得touchesMoved:事件。

我的需求: A内的子视图应始终优先接收,我不希望触摸并等待。如果我触摸A而不是它的子视图,我希望UIScrollView接收到触摸事件,如平移和移动(contentSize大于frame)。


编辑 1.0:

我将A视图放在通用的UIScrollView中的唯一原因是,我希望能够对A视图进行缩放。因此,我正在执行以下操作:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return customView; // this is the A view
}

一开始我没有将A视图放在UIScrollView内,我所做的只是将A作为UIViewController的根视图的子视图添加进去,一切都进行得很顺利。如果有其他方法可以启用缩放功能,我会很乐意接受答案。


仅仅通过快速谷歌搜索,尝试重写scrollView的'hitTest'方法?http://www.herbert-siojo.com/2011/04/01/forwarding-touches-to-subviews-of-uiscrollview/ - A-Live
已经看到这个建议了,但不起作用。 - Rui Peres
1
你尝试过在UIScrollView中添加UIGestureRecognizer并检查触摸是否在自定义UIView中的locationInView中吗? - Mina Nabil
A视图覆盖了整个屏幕。因此,A屏幕将始终被返回。 - Rui Peres
4个回答

8

注意:感谢大家的贡献,特别感谢Aaron Hayman。

我通过在我的UIScrollView子类上执行以下操作来解决问题:

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
   CGPoint pointOfContact = [gestureRecognizer locationInView:self];

    // The view with a tag of 200 is my A view.
    return (![[self hitTest:pointOfContact withEvent:nil] isEqual:[self viewWithTag:200]]);
}

2
很好,我应该推荐使用UIScrollView的子类。但我要补充一点,你可能需要区分UIScrollView的手势识别器。例如,您可能希望取消轻拍和平移,但不是捏合(缩放)。当locationInView返回一个子视图中的点时,偶尔无法捏合可能会让人感到不安。您可以使用以下代码轻松实现:if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]) return YES; - Aaron Hayman

4

我没有测试过,但我认为您在视图A(或其子视图)中处理触摸事件的方式将决定如何传递触摸事件。具体来说,如果您尝试使用方法:touchesBegantouchesMovestouchesEnded等而不是使用UIGestureRecognizer,则无法按照您想要的方式接收到触摸事件。苹果设计UIGestureRecognizer以解决您所面临的问题。具体来说,UIScrollView使用UIPanGestureRecognizer来处理滚动。如果您在View A的每个子视图中添加UIPanGestureRecognizer,则任何一个子视图上发生的“平移”都应该发送到该子视图,而不是UIScrollView。但是,如果您只是使用“原始”的touches方法,则UIScrollView中的UIPanGestureRecognizer永远不会被取消。

总的来说,最好始终使用UIGestureRecognizer而不是直接在视图中处理触摸操作。如果您需要以没有标准UIGestureRecognizer可以提供的方式处理触摸操作,请子类化UIGestureRecognizer并在那里处理触摸操作。这样,您即可获得所有UIGestureRecognizer的功能,同时还能进行自定义触摸处理。我真的认为苹果打算让UIGestureRecognizer替代开发人员在UIView上使用的大多数(如果不是全部)自定义触摸处理代码。它允许代码重用,并且在缓解哪些代码处理哪些触摸事件时更容易处理。


1

Jacky,我需要类似的东西:在建筑平面图(你的A,在我的情况下是UIScrollView的子类)中,让用户放置和调整对象(称之为B)。这是我为了实现这种行为所需的草图:

在父视图(A)的initWithFrame:方法中,设置以下两个属性:
self.canCancelContentTouches = YES;
self.delaysContentTouches = NO;
这将确保对B的轻击立即传输到B。
在嵌入的B中,停止父视图A取消触摸事件,以便它不会干扰在B上开始的手势。 在touchesBegan:方法中,沿着视图层次结构向上搜索(使用视图的superview属性),直到找到一个UIScrollView,并将其canCancelContentTouches属性设置为NO。记住您更改的父视图,并在B的touchesEndedtouchesCancelled方法中恢复此属性。

我很想知道这对你是否有效。祝你好运!

nobi


我以前做过这个,而且效果很好。问题在于我想使用平移手势来实现缩放功能。为此,我需要将 A 视图放入 UIScrollView 中,以便能够调用此方法:- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; - Rui Peres

0

我认为你最好使用"touchesBegan,touchesMoved,touchesEnded"来传递事件。

你可以这样做:

你应该创建一个主视图。它有两个属性。一个是你的滚动视图A,另一个是你的自定义视图。

`[yourScrollView addSubviews:yourCustomView];
[mainView addSubviews:yourScrollView];`

然后在mainView.m中编写你的touches方法,像这样(忽略scrollView语句)

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
   if ([[touches allObjects] isKindOfClass:[yourCustomView class]]) 
   {

        //do whatever you want
   }

}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
   if ([[touches allObjects] isKindOfClass:[yourCustomView class]]) 
   {
       //do whatever you want
   }
}

最后一步:将事件传递给scrollView的子视图(即A) 。
#import "yourScrollView.h"


@implementation yourScrollView


- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code.
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesMoved:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesEnded:touches withEvent:event];
    if(!self.dragging)
        [[self nextResponder] touchesEnded:touches withEvent:event];
}

- (void)dealloc {
    [super dealloc];
}


@end 

希望能够帮助你


我有一个带有自定义UIView A的UIScrollView。自定义UIView被称为A,而不是UIScrollView。这个UIScrollView只是用来让我能够放大/缩小A。 - Rui Peres
直接将事件发送给下一个响应者是苹果不鼓励的。 - Pascalius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接