UIGestureRecognizer阻止子视图处理触摸事件

时间:2011-03-07 17:32:06

标签: objective-c ios uiview uigesturerecognizer uiresponder

我正在试图弄清楚如何以正确的方式完成。我试图描绘这种情况: enter image description here

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

我已经使用以下代码,但它显然不是一个很好的解决方案,我相信有更好的方法。这是放在UIView(超级视图)中:

-(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];
}

10 个答案:

答案 0 :(得分:180)

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

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

答案 1 :(得分:105)

将触摸事件阻止到子视图是默认行为。您可以更改此行为:

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

答案 2 :(得分:5)

我正在显示一个有自己的tableview的下拉子视图。因此,touch.view有时会返回UITableViewCell等类。我不得不单步执行超类(es)以确保它是我认为的子类:

-(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;
}

答案 3 :(得分:4)

一种可能性是将您的手势识别器子类化(如果您还没有)并覆盖-touchesBegan:withEvent:,以便确定每次触摸是否在排除的子视图中开始,如果是,则调用-ignoreTouch:forEvent:进行触摸那样。

显然,您还需要添加一个属性来跟踪被排除的子视图,或者更好的是,排除子视图数组。

答案 4 :(得分:4)

建立在@Pin Shih Wang 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)
    }
}

答案 5 :(得分:2)

可以不继承任何类。

你可以在手势的回调选择器

中查看gestureRecognizers

如果view.gestureRecognizers不包含您的gestureRecognizer,请忽略它

例如

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

检查view.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
    }
}

答案 6 :(得分:1)

我创建了一个UIGestureRecognizer子类,用于阻止附加到特定视图的超视图的所有手势识别器。

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

答案 7 :(得分: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中释放它。

答案 8 :(得分:0)

我也在做一个弹出窗口,这就是我做的方式

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

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

答案 9 :(得分:0)

为parentView的所有识别器实现委托,并将gestureRecognizer方法放在委托中,该委托负责同时触发识别器:

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

}

如果你想让孩子被触发而不是父识别器,你可以使用失败方法:

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