手势识别器和按钮动作

我有一个视图层次结构,看起来像这样:

UIView (A)
UIView > UIImageView
UIView > UIView (B)
UIView > UIView (B) > Rounded Rect Button
UIView > UIView (B) > UIImageView
UIView > UIView (B) > UILabel

我已经附加了手势识别器到我的 UIView (B)。我面临的问题是,我没有得到任何行动的圆角矩形按钮内的 UIView (B)。SingleTap 手势识别器捕获/覆盖按钮的 Touch Up Inside 事件。

我该怎么做?我认为,响应链层次结构将确保按钮触摸事件将被优先考虑,它会得到触发!我错过了什么?

下面是一些相关代码:

#pragma mark -
#pragma mark View lifecycle (Gesture recognizer setup)


- (void)viewDidLoad {
[super viewDidLoad];


// double tap gesture recognizer
UITapGestureRecognizer *dtapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapGestureRecognizer:)];
dtapGestureRecognize.delegate = self;
dtapGestureRecognize.numberOfTapsRequired = 2;
[self.viewB addGestureRecognizer:dtapGestureRecognize];


// single tap gesture recognizer
UITapGestureRecognizer *tapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapGestureRecognizer:)];
tapGestureRecognize.delegate = self;
tapGestureRecognize.numberOfTapsRequired = 1;
[tapGestureRecognize requireGestureRecognizerToFail:dtapGestureRecognize];
[self.viewB addGestureRecognizer:tapGestureRecognize];


// add gesture recodgnizer to the grid view to start the edit mode
UILongPressGestureRecognizer *pahGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecognizerStateChanged:)];
pahGestureRecognizer.delegate = self;
pahGestureRecognizer.minimumPressDuration = 0.5;
[self.viewB addGestureRecognizer:pahGestureRecognizer];


[dtapGestureRecognize release];
[tapGestureRecognize release];
[pahGestureRecognizer release];
}


#pragma mark -
#pragma mark Button actions


- (IBAction)buttonTouchUpInside:(id)sender {
NSLog(@"%s, %@", __FUNCTION__, sender);
}


#pragma mark -
#pragma mark Gesture recognizer actions




- (void)singleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
NSLog(@"%s", __FUNCTION__);
}


- (void)doubleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
NSLog(@"%s", __FUNCTION__);
}


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


switch (gestureRecognizer.state) {


case UIGestureRecognizerStateEnded: {
NSLog(@"%s", __FUNCTION__);


break;
}
default:
break;
}
}
85645 次浏览

In the "shouldReceiveTouch" method you should add a condition that will return NO if the touch is in the button.

This is from apple SimpleGestureRecognizers example.

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


// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourButton)) {//change it to your condition
return NO;
}
return YES;
}

hope it will help

Edit

As Daniel noted you must conform to UIGestureRecognizerDelegate for it to work.

shani

I also had the same problem , then i tried with

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


if ([touch.view isKindOfClass:[UIButton class]]) {      //change it to your condition
return NO;
}
return YES;
}

It is working now perfectly.........

Generally speaking, we use below delegate method to avoid the touch in all kinds of UIControls:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (([touch.view isKindOfClass:[UIControl class]])) {
return NO;
}
return YES;
}

Note: DO NOT do this check (check the recognizer.view class type) the gestureRecognizerShouldBegin, it won't work.

The best solution is to my mind using code snippet below:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint touchLocation = [touch locationInView:self.view];
return !CGRectContainsPoint(self.menuButton.frame, touchLocation);
}

Here is a Swift version:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if (touch.view!.isKindOfClass(UIButton)) {
return false
}
return true
}

Don't forget to:

  1. Make you class conform to UIGestureRecognizerDelegate
  2. Make your tapper object delegate to self (e.g: tapper.delegate = self)

Here is a Swift 3.0 version:

extension UIViewController: UIGestureRecognizerDelegate {


public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view is UIButton {
return false
}
return true
}

Don't forget to:

Make your tapper object delegate to self (e.g: tapper.delegate = self)

Swift version of Vivienne answer:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let location = touch.location(in: view)
return !someView.frame.contains(location)
}

for Swift 5 inside the delegate you can just add:

return (touch.view is UIButton) ? false : true

My case was the following :

UIView (with tap gesture)
├-> UIView (named "textFieldView")
├-> UIControl
├-> UITextField
├-> UIView (over the textField, with a tap gesture)


╭──────────────────────────────╮
│ ╭──────────────────────────╮ │
│ │ ╭──╮  ┌╴╶╴╶╴╶╴╶╴╶╴╶╴╶╴╶┐ │ │
│ │ ╰──╯  └╴╶╴╶╴╶╴╶╴╶╴╶╴╶╴╶┘ │ │
│ ╰──────────────────────────╯ │
└──────────────────────────────┘

The tapGesture on the upper UIView was causing a conflict with the UIControl target. The target was called if the target was setup with touchDown (but not called for touchUpInside) or if I was changing the inheritance from UIControl to UIButton while keeping the target setup on event touchUpInside.

What I do to solve that is : upper view is setup as delegate of its tap gesture. Function func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool is implemented (in upper UIView) like that :

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let touchLocationRelatedToTextFieldView = touch.location(in: textFieldView)
return textFieldView.frame.contains(touchLocationRelatedToTextFieldView) == false
}

With that delegate function implemented I'm now able to keep my UIControl and the target setup on touchUpInside! :)