2011-03-07 210 views
72

我想弄清楚如何完成正确的方式。我试图描绘的情况: enter image description hereUIGestureRecognizer块子视图处理触摸事件

我加入一个UITableView作为UIView的子视图。 UIView响应tap和pinchGestureRecognizer,但是当这样做时,tableview停止对这两个手势作出反应(它仍然对滑动作出反应)。

我已经使它与下面的代码一起工作,但它显然不是一个很好的解决方案,我相信有更好的方法。这是摆在UIView(SuperView把):

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 
    if([super hitTest:point withEvent:event] == self) { 
     for (id gesture in self.gestureRecognizers) { 
      [gesture setEnabled:YES]; 
     } 
     return self; 
    } 
    for (id gesture in self.gestureRecognizers) { 
     [gesture setEnabled:NO]; 
    } 
    return [self.subviews lastObject]; 
} 

回答

175

我有一个非常类似的问题,发现我的解决方案in this SO question。总之,将自己设置为您的UIGestureRecognizer的代表,然后在允许识别器处理触摸之前检查目标视图。相关的代理方法是:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch 
+3

我喜欢这个解决方案最大,因为它不涉及弄乱触摸,'hitTest:withEvent:'或'pointInside:withEvent:'。 – DarkDust 2011-07-25 10:26:20

+6

干净的解决方案,您可以使用例如'return!(touch.view == givenView);'如果你只是想排除一个给定的视图或'返回!(touch.view.tag == kTagNumReservedForExcludingViews);'当你想停止你的识别器处理触摸整个一堆不同的子视图。 – cate 2012-12-29 22:40:48

+6

我会用' - (BOOL)isDescendantOfView:(UIView *)view'做命中测试。这在' - (void)touchesBegan:(NSSet *)与UIEvent:(UIEvent *)事件'在UISestureRecognizer的子类中触及时也可以正常工作。 – Christoph 2013-03-13 22:27:13

4

一种可能性是继承你的手势识别(如果你还没有的话),并覆盖-touchesBegan:withEvent:使得它确定每个触摸是否开始在排除的子视图和如果有的话,请拨打-ignoreTouch:forEvent:

很明显,您还需要添加一个属性以跟踪排除的子视图,或者更好的方式是排除一些子视图。

+0

有代码堆在这里https://github.com/search?l=objective-c&q=uigesturerecognizer&ref=cmdform&type=Code – johndpope 2013-05-15 00:11:58

92

将触摸事件阻塞到子视图是默认行为。你可以改变这种行为:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)]; 
r.cancelsTouchesInView = NO; 
[agentPicker addGestureRecognizer:r]; 
+4

这将触发事件发送到子视图,但它也将被发送到手势识别器。所以这将防止子视图的阻塞,但手势识别器仍然会在子视图上被识别。 – 2012-08-31 09:38:36

+0

@Jonathan。是的,我同意你的意见。我在我的手势处理器方法中做的是检查手势位置是否发生在相关的子视图内,在这种情况下,我不需要执行代码的其余部分。另外,我选择这个解决方法的原因之一是UITapGestureRecognizer没有声明'translationInView'方法。因此,实现上面提到的UIGestureRecognizerDelegate方法只会导致崩溃错误'...无法识别的选择器发送到等等。要检查,请使用如下内容:'CGRectContainsPoint(subview.bounds,[识别器locationInView:子视图])'。 – MkVal 2014-01-30 09:00:03

+0

根据OP的问题,应该选择这个答案作为主要答案。 – farzadshbfn 2017-12-05 13:07:42

0

你可以将其关闭并....在我的代码我做了这样的事情,我需要把它关掉,当键盘不显示,就可以用它您的情况:

通话,这是viewDidLoad中等等:

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil]; 
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil]; 

然后创建两个方法:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=true; // turn the gesture on 
} 

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=false; //turn the gesture off so it wont consume the touch event 
} 

这样做会禁用水龙头。我不得不将tap变成一个实例变量,然后在dealloc中释放它。

4

我正在显示一个有自己的tableview的下拉式子视图。因此,touch.view有时会返回像UITableViewCell类。我必须通过超类来确保它是我认为它的子类:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
{ 
    UIView *view = touch.view; 
    while (view.class != UIView.class) { 
     // Check if superclass is of type dropdown 
     if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own 
      NSLog(@"Is of type dropdown; returning NO"); 
      return NO; 
     } else { 
      view = view.superview; 
     } 
    } 

    return YES; 
} 
+0

while循环占了那个 – joslinm 2016-08-17 20:02:09

2

有可能没有继承任何类。

您可以检查gestureRecognizers手势的回调选择

如果view.gestureRecognizers不包含您gestureRecognizer,只是忽略它

例如

- (void)viewDidLoad 
{ 
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(handleSingleTap:)]; 
    singleTapGesture.numberOfTapsRequired = 1; 
} 

检查视图。gestureRecognizers这里

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer 
{ 
    UIEvent *event = [[UIEvent alloc] init]; 
    CGPoint location = [gestureRecognizer locationInView:self.view]; 

    //check actually view you hit via hitTest 
    UIView *view = [self.view hitTest:location withEvent:event]; 

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) { 
     //your UIView 
     //do something 
    } 
    else { 
     //your UITableView or some thing else... 
     //ignore 
    } 
} 
+0

正如在其他答案中提到的,如果使用这种方法确保点击手势识别器将点击转发到您的视图层次: 'singleTapGesture.cancelsTouchesInView = NO;'添加到上面的'viewDidLoad' – 2017-09-27 12:14:16

1

我创建了一个子类UIGestureRecognizer设计用于阻止附接至一个特定的视图的所有superviews手势识别。

这是我的WEPopover项目的一部分。你可以找到它here

0

我也做酥料饼的,这是我做的

func didTap(sender: UITapGestureRecognizer) { 

    let tapLocation = sender.locationInView(tableView) 

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) { 
     sender.cancelsTouchesInView = false 
    } 
    else { 
     delegate?.menuDimissed() 
    } 
} 
0

实现委托的parentView的所有识别器,并把gestureRecognizer方法,它负责识别的同时触发委托:

func gestureRecognizer(UIGestureRecognizer,  shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool { 
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) { 
    return true 
    } else { 
    return false 
} 

}

U可以使用故障方法如果u想使孩子们被触发,但不是父识别:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

1

大厦@Pin施王answer。我们忽略除包含轻击手势识别器的视图以外的所有轻敲。正如我们设置tapGestureRecognizer.cancelsTouchesInView = false一样,所有轻敲都会正常转发到视图层次结构。这里是Swift3/4的代码:

func ensureBackgroundTapDismissesKeyboard() { 
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) 
    tapGestureRecognizer.cancelsTouchesInView = false 
    self.view.addGestureRecognizer(tapGestureRecognizer) 
} 

@objc func handleTap(recognizer: UIGestureRecognizer) { 
    let location = recognizer.location(in: self.view) 
    let hitTestView = self.view.hitTest(location, with: UIEvent()) 
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) { 
     // I dismiss the keyboard on a tap on the scroll view 
     // REPLACE with own logic 
     self.view.endEditing(true) 
    } 
}