33

我有一个包含多个“子视图”的 UIView(“容器视图”)。我想在容器视图中添加一个 UITapGestureRecognizer,这样当我触摸容器视图内部但子视图外部的区域时,它就会被激活。

目前,触摸容器视图内的任何位置,包括子视图内的任何位置都会激活手势识别器。

实现看起来像这样: 在控制器中:

ContainerView *containerView = [[ContainerView alloc] initWithSubViews:array];
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc] initWithTarget:self action:@selector(someSelector)];
[containerView addGestureRecognizer:tap];
[self.view addSubView:containerView];

在 ContainerView.m 中

-(id)initWithSubviews:(NSArray *)array {
    for (subView *s in array) {
        [self addSubView:s];
    }
    return self;
}

我认为出现问题是因为在子视图之后添加了手势识别器。如果这是真的,那么解决方案将需要将 initWithSubViews 方法分成两个单独的方法,我希望避免这样做。

谢谢你

4

9 回答 9

42

我使用了下面的简单方法。它可以正常工作!

实现UIGestureRecognizerDelegate函数,只接受superview的touch,不接受subview的touch:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view != _mySuperView) { // accept only touchs on superview, not accept touchs on subviews
        return NO;
    }

    return YES;
}
于 2013-09-19T04:45:42.133 回答
15

iOS 6 引入了一个很好的新功能来解决这个确切的问题 - UIView(子视图)可以从gestureRecognizerShouldBegin:(附加到超级视图的手势识别器)返回 NO。实际上,对于某些手势识别器,这已经是某些 UIView 子类的默认设置(例如,与附加到超级视图的 UITapGestureRecognizer 相关的 UIButton)。

请参阅我关于此主题的书:http ://www.aeth.com/iOSBook/ch18.html#_gesture_recognizers

于 2013-03-18T04:27:17.897 回答
13

我设法通过执行以下操作使其工作:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)];

// ...

-(void) tapGestureHandler:(UITapGestureRecognizer *)sender {
    CGPoint point = [sender locationInView:sender.view];
    UIView *viewTouched = [sender.view hitTest:point withEvent:nil];
    if ([viewTouched isKindOfClass:[ThingIDontWantTouched class]]) {
        // Do nothing;
    } else {
        // respond to touch action
    }
}
于 2013-03-18T03:51:23.960 回答
6

记得添加和设置委托方法-

在你的 UIViewController 的 .h 文件中包含这个委托

@interface YourViewController: UIViewController<UIGestureRecogniserDelegate>

然后在哪里创建你的 tapGesture 对象,(例如在 viewDidLoad 中)

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(actionMethodYouWantToHappenOnTap)];  

    tap.delegate = self;  //Remember to set delegate! Otherwise the delegate method won't get called.

    [self.view addGestureRecognizer:tap];

记住设置委托方法tap.delegate = self;,然后点击的委托方法现在将在点击时触发。

在此方法中,当您希望使用点击手势识别时,您可以处理,在我的代码中,如果特定子视图可见,我希望它忽略点击

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

     if (!mySubview.hidden){
     return NO;   //This fired if said subview was visible, though whatever the condition of where and when you want tap to work, can be handled within this delegate method.
     }

    return YES;
}

我希望这可以帮助其他有同样问题的人。记住设置我发现的委托方法是很容易被忽视的。

干杯

于 2014-09-15T11:55:28.037 回答
3

很多答案,添加另一个替代解决方案。将您不想接收触摸的子视图的视图标签初始化为 1 并执行以下操作:

- (void)viewDidLoad {
    [super viewDidLoad];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tap.delegate = self;
    [self.view addGestureRecognizer:tap];
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view.tag == 1) {
        return NO;
    }
    return YES;
}

-(void)handleTap:(id)sender
{
//Handle tap...
}

这样,触摸手势将仅从您需要它的视图中接收。

于 2016-09-22T11:24:22.173 回答
1

更新到Swift 5

@Awais Hussain 的回答非常适合,

用手势查看 -> Controller.view -> subView -> ViewThingI_DontWantToBeTouched

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapGestureHandler(_:)))

    }


    @objc
    func tapGestureHandler(_ sender: UITapGestureRecognizer){
        let point = sender.location(in: sender.view)
        if let viewTouched = sender.view?.hitTest(point, with: nil), viewTouched is ViewThingI_DontWantToBeTouched{
            ()
            // Do nothing;
        }
        else {
            // respond to touch action
        }
    }
}
于 2020-06-05T09:12:55.630 回答
0

如果您UITapGestureRecognizer为 ContainerView 添加,它应该响应整个 ContainerView。它不会介意它的子视图。

检查手势位置,如果它在您的子视图位置,只需跳过手势动作。

- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {


    CGPoint tapPoint = [gestureRecognizer locationInView:nil]; 

   //check your tapPoint contains ur subview frame, skip.else do ur actions
}
于 2013-03-17T03:46:58.257 回答
0

添加:

我只是想到了更好的东西。

在你的处理程序中

-(void)tapGestureHandler:(UITapGestureHandler)gesture

检查gesture.view是否是超级视图。如果点击子视图,它将返回子视图。

==================================================== ==

我建议在超级视图中覆盖。

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

调用它来确定手势是否落在视图内

请参阅此问题的答案以了解其默认行为方式。

iOS 的事件处理 - hitTest:withEvent: 和 pointInside:withEvent: 是如何相关的?

您可以检查该点是否在其任何子视图中。如果是,则返回 nil,否则返回 self。

或者

在每个子视图中,添加一个不执行任何操作的 Tapgesture 识别器。默认情况下,子视图的手势识别器将取消其父视图的手势识别器。如果有很多子视图,我会留意内存占用。

于 2013-03-17T06:38:32.703 回答
0

Swift 4 的更新答案

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view != yourView
        {
            return false
        }

        return true
    }

在此之前,请确保您已将委托属性设置为您的对象..

于 2018-05-25T03:54:47.767 回答