UIScrollView触摸事件与子视图触摸事件的区别

7
请有人能帮助一个新手吗?我在不同的论坛上发布了这个问题,但没有得到答案,虽然搜索其他答案时发现了stackOverflow,所以我希望这里可以得到解答。
我有一个BeachView.h(UIScrollView子类,图片是沙滩)上覆盖着随机数量的Stone.h(UIImageView子类,一个随机的石头PNG,userInteractionEnabled = YES接受触摸)。
如果用户在海滩上触摸并移动,它应该滚动。 如果用户点击石头,它应该调用方法“touchedStone”。 如果用户点击海滩上没有石头的地方,它应该调用方法“touchedBeach”。
现在,我意识到这听起来很简单。每个人和一切都告诉我,如果在UIScrollView上有可以接受触摸的东西,那么它应该将控制权传递给它。所以当我触摸并拖动时,它应该滚动;但是如果我点击,并且它在一个石头上,它应该忽略海滩的点击并接受石头的点击,对吗?
然而,似乎两个视图都接受触摸并同时调用touchedStone和touchedBeach。此外,海滩点击先发生,因此我甚至无法放置“如果触摸了石头,则不要运行touchedBeach”类型的标志。
这是一些代码。 在BeachView.m上:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     if (self.decelerating) { didScroll = YES; }
        else { didScroll = NO; }

     UITouch *touch = [[event allTouches] anyObject];
     CGPoint touchLocation = [touch locationInView:touch.view];
     NSLog(@"touched beach = %@", [touch view]);
     lastTouch = touchLocation;
     [super touchesBegan:touches withEvent:event];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
     didScroll = YES;
     [super touchesMoved:touches withEvent:event];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
     if (didScroll == NO && isPaused == NO) { 
          [self touchedBeach:YES location:lastTouch];
     }
     [super touchesEnded:touches withEvent:event];
}

关于Stone.m


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     [parent stoneWasTouched]; // parent = ivar pointing from stone to beachview
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *touch = [[event allTouches] anyObject];
     CGPoint touchLocation = [touch locationInView:touch.view];
     NSLog(@"touched stone = %@", [touch view]);
     [parent touchedStone:YES location:touchLocation];
}

经过一次 stone tap 后,我的 NSLog 看起来像这样:

Touched beach = <BeachView: 0x1276a0>
ran touchedBeach
Touched Stone = <Stone: 0x1480c0>
ran touchedStone

实际上它正在同时运行两者。更奇怪的是,如果我将Stone.m中的touchesBegan和touchesEnded移除但保留userInteractionEnabled = YES,则beachView自己注册了两个触摸,但返回的视图是Stone(第二次触摸)。


Touched beach = <BeachView: 0x1276a0>
ran touchedBeach
Touched beach = <Stone: 0x1480c0>
ran touchedBeach

非常希望您能帮我解决这个问题,我已经尝试了好几天。如何让点击的石头只调用touchedStone,而点击的海滩只调用touchedBeach?我做错了什么吗?

4个回答

3

确实如此,iPhone SDK 3.0及以上版本不再将触摸传递给-touchesBegan:-touchesEnded:的**UIScrollview**子类方法。您可以使用touchesShouldBegintouchesShouldCancelInContentView方法来代替。

如果您真的想获取这些触摸事件,有一个hack可以实现。

在您的UIScrollView子类中覆盖hitTest方法,像这样:

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

  UIView *result = nil;
  for (UIView *child in self.subviews)
    if ([child pointInside:point withEvent:event])
      if ((result = [child hitTest:point withEvent:event]) != nil)
        break;

  return result;
}

这将传递给您的子类触摸事件,但是您无法取消 UIScrollView 超类的触摸事件。

2
在iPhone OS 3.0之前,UIScrollView的hitTest:withEvent:方法总是返回self,以便直接接收UIEvent,只有在它确定与滚动或缩放无关时才将其转发到适当的子视图。
我不能对iPhone OS 3.0发表评论,因为它受到NDA的限制,但请查看您的“iPhone SDK Release notes for iPhone OS 3.0 beta 5” :)
如果您需要针对pre-3.0进行定位,可以在BeachView中重写hitTest:withEvent:并设置一个标志,以忽略下一个海滩触摸,如果CGPoint实际上在石头中。
但是,您是否尝试过将对[super touches *:withEvent:]的调用从覆盖的方法末尾移动到开头?这可能会导致石头轻击首先发生。

我把[super]移到了方法的开头,这起到了作用。它仍然注册两次点击,但至少现在它会说1-beganBeach 2-beganStone 3-endedBeach 4-endedStone,所以有时间在那里弹出一个标志。我只是在看hitTest,但当它开始谈论CAlayers vs UIviews时,它似乎像一本我还没有准备好打开的书。我会再试一次。谢谢Hatfinch, -k. - Kevin Beimers

0
我曾经遇到过一个类似的问题,针对一个simpleView添加了一个scrollView,每当我点击simpleView时,scrollView会获取到触摸事件,而不是simpleView,结果scrollView会滚动。为了避免这种情况,我在用户点击simpleView时禁止了scrollView的滚动,否则则启用滚动。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    UIView *result = [super hitTest:point withEvent:event] ;
    if (result == simpleView)
    {
        scrollView.scrollEnabled = NO ;
    }
    else
    {
        scrollView.scrollEnabled = YES ;
    }

    return result ;

}

0

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