UITapGestureRecognizer中断UITableView didSelectRowAtIndexPath


207

我编写了自己的函数,以在键盘出现时向上滚动文本字段。为了通过远离文本字段来关闭键盘,我创建了一个UITapGestureRecognizer,在单击时会在文本字段上退出第一响应者。

现在,我还为UITableView文本字段创建了一个自动完成功能,该功能会在文本字段的下方创建一个自动完成功能,并在用户输入文本时在其中填充项目。

但是,当选择自动完成表中的一项时,didSelectRowAtIndexPath不会被调用。取而代之的是,似乎轻击手势识别器正在被调用,只是辞职了第一响应者。

我猜想有某种方法可以告诉轻击手势识别器将轻UITableView击消息继续传递到,但是我不知道它是什么。任何帮助将不胜感激。


Answers:


258

好的,经过对手势识别器文档的搜索后,终于找到了它。

解决方案是实施UIGestureRecognizerDelegate并添加以下内容:

////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods

#pragma mark UIGestureRecognizerDelegate methods

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:autocompleteTableView]) {

        // Don't let selections of auto-complete entries fire the 
        // gesture recognizer
        return NO;
    }

    return YES;
}

这样就好了。希望这也会对其他人有所帮助。


您可能还需要跳过对文本字段的点击。
史蒂文·克雷默

5
我还有更多的运气return ![touch.view isKindOfClass:[UITableViewCell class]];
乔什·帕拉多里德

1
@Josh如果您单击单元格内部的标签,则此方法不起作用。实际上,Jason的答案非常适合我的需求。
威廉T.

但这不是您问题的答案,不是吗?这是一个“或”。基本上,对表格视图的任何触摸对于手势识别器来说都是微不足道的....您想要的是手势识别器和表格视图选择都可以起作用
PostCodeism

5
@Durgaprasad要禁用手势识别显式地选择所述细胞当我添加了一个 ![touch.view isEqual: self.tableView] &&[touch.view isDescendantOfView: self.tableView]排除tableView本身(因为isDescendantOfView:也返回YES如果参数视图是接收机视野本身)。希望对其他希望获得相同结果的人有所帮助。
anneblue 2015年

220

解决此问题的最简单方法是:

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

这样一来,您就可以UIGestureRecognizer识别点击,并将触摸信息传递给下一个响应者。此方法的意外结果是,如果您UITableViewCell在屏幕上按下另一个视图控制器。如果用户点击该行以关闭键盘,则键盘和按键都将被识别。我怀疑这是您想要的,但是这种方法在许多情况下都足够。

同样,扩展罗伯特的答案,如果您有一个指向相关表视图的指针,那么您可以直接比较其类,而不必转换为字符串,并希望苹果公司不要更改其命名法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch
{
    if([touch.view class] == tableview.class){
        return //YES/NO
    }

    return //YES/NO

}

请记住,还必须声明,UIGestureRecognizer以在其中包含此代码的委托。


4
谢谢,setCancelsTouchesInView改变了一切:)
PostCodeism

可能仍要使用,isDescendantOfView而不是仅检查s是否class相等,因为您可能正在点击子视图。取决于您的需要。
shim

71

cancelsTouchesInView您的识别器设置为false。否则,它会自己“消耗”触摸,并且不会将其传递给表格视图。这就是选择事件从未发生的原因。

例如在 swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)

1
非常感谢!
碧山

1
这是Swift中最干净的。谢谢
Dx_

在表格视图中的单元格中效果很好,既可爱又简单!
Abdoelrhman

@laoyur非常正确
bLacK hole

仍然获得了单元格点击,而不是点击手势
famfamfam

42

对于Swift(基于@Jason的回答):

class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate

private var tap: UITapGestureRecognizer!

override func viewDidLoad() {
   super.viewDidLoad()

   self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
   self.tap.delegate = self
   self.view.addGestureRecognizer(self.tap)
}

// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view?.isDescendantOfView(self.tableView) == true {
        return false
    }
    return true
}

非常感谢,这节省了我的时间。
碧山

22

我可能有更好的解决方案,可以在表格视图上添加轻击手势,但可以同时选择单元格:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.locationInView(tableView)
        return (tableView.indexPathForRowAtPoint(location) == nil)
    }
    return true
}

我只是在用户点击屏幕的位置寻找一个单元格。如果找不到索引路径,则我让手势接收触摸,否则我将其取消。对我来说,效果很好。


1
这个解决方法很糟糕!非常有用的从表中的空字段区分小区..
亚历山德罗Ornano

18

我觉得没有必要只是单纯的若干组代码写入块 cancelsTouchesInViewfalse你的姿势对象,默认情况下它true,你只需要设置它false。如果您UITapGesture在代码中使用对象并且还使用UIScrollView(tableview,collectionview),则设置此属性false

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)

感谢您发布此答案
Gustavo Baiocchi Costa

14

类似的解决方案是实施 gestureRecognizer:shouldReceiveTouch:使用视图的类以确定要采取的操作。这种方法的优点是不会在直接围绕表格的区域中屏蔽抽头(这些区域的视图仍来自UITableView实例,但它们不表示单元格)。

这还有一个好处,它可以在一个视图上使用多个表(无需添加额外的代码)。

注意:有一种假设认为Apple不会更改类名。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}

1
要消除类名的字符串,请使用此行return ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

对于Swift 4.2,解决方案是实施UIGestureRecognizerDelegate并添加以下内容:

extension ViewController : UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view!.isDescendant(of: tblView) {
            return false
        }
        return true
    }
}

当您单击表视图时,此委托方法将返回false,并且didSelectRowAtIndexPath表视图的方法可以正常工作。


1
感谢好友发布此答案,您可以节省我的时间。
杰伊·巴拉尼

4

我有另外一种情况,我希望当用户在表格视图之外点击时才调用触摸手势功能。如果用户在表格视图中点击,则不应调用触摸手势功能。此外,如果调用了触摸手势功能,它仍应将触摸事件传递给被点击的视图,而不是使用它。

生成的代码是Abdulrahman Masoud的答案和Nikolaj Nielsen的答案的结合。

extension MyViewController: UIGestureRecognizerDelegate {
    func addGestureRecognizer() {
        let tapOnScreen = UITapGestureRecognizer(target: self,
                                                action: #selector(functionToCallWhenUserTapsOutsideOfTableView))

        // stop the gesture recognizer from "consuming" the touch event, 
        // so that the touch event can reach other buttons on view.
        tapOnScreen.cancelsTouchesInView = false

        tapOnScreen.delegate = self

        self.view.addGestureRecognizer(tapOnScreen)
    }

    // if your tap event is on the menu, don't run the touch event.
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view?.isDescendant(of: self.tableView) == true {
            return false
        }
        return true
    }

    @objc func functionToCallWhenUserTapsOutsideOfTableView() {

        print("user tapped outside table view")
    }
}

而在MyViewController类,它具有类的UITableView,在onViewDidLoad(),我确信打电话addGestureRecognizer()

class MyViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.addGestureRecognizer()
        ...
    }
    ...
}

3

只需将设置cancels touches in viewfalse以下任何手势即可(table/collection)View

-Interfacebuilder

接口构建器

-代码

<#gestureRecognizer#>.cancelsTouchesInView = false

如果我想禁用它,不仅允许didSelectRowAt与它一起工作,该怎么办?
Eyad Shokry

1

虽然太晚了,许多人发现上述建议可以正常工作,但我无法让Jason或TMilligan的方法起作用。

我有一个带有多个包含textField的单元格的Static tableView,该单元仅使用数字键盘接收数字输入。这对我来说是理想的:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if(![touch.view isKindOfClass:[UITableViewCell class]]){

        [self.firstTF resignFirstResponder];
        [self.secondTF resignFirstResponder];
        [self.thirdTF resignFirstResponder];
        [self.fourthTF resignFirstResponder];

        NSLog(@"Touches Work ");

        return NO;
    }
    return YES;
}

确保已<UIGestureRecognizerDelegate>在.h文件中实现了此功能。

此行![touch.view isKindOfClass:[UITableViewCell class]]检查是否已点击tableViewCell并关闭了任何活动的键盘。



0

这是我的解决方案,它将识别器的shouldReceiveTouch直接与键盘是否在显示有关。

在您的点击手势识别器委托中:

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([PFXKeyboardStateListener sharedInstance].visible) {
        return YES;
    }

    return NO;
}

PFXKeyboardStateListener.h

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

@property (nonatomic, readonly, getter=isVisible) BOOL visible;

@end

PFXKeyboardStateListener.m

static PFXKeyboardStateListener *sharedInstance;

@implementation PFXKeyboardStateListener

+ (PFXKeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    @autoreleasepool {
        sharedInstance = [[self alloc] init];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

您可能想要更新键盘侦听器的单例模式,但我还没有得到。希望这对其他人也适用,对我也适用。^^


0

为UIGestureRecognizer的委托实现此方法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
  UIView *superview = touch.view;
  do {
    superview = superview.superview;
    if ([superview isKindOfClass:[UITableViewCell class]])
      return NO;
  } while (superview && ![superview isKindOfClass:[UITableView class]]);

  return superview != nil;
}


0

我的情况有所不同,包括针对self.view的uisearchbar和uitableview。我想通过触摸视图来关闭uisearchbar键盘。

var tapGestureRecognizer:UITapGestureRecognizer?

override func viewWillAppear(animated: Bool) {
    tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}

在UISearchBar委托方法上:

func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
    view.addGestureRecognizer(tapGestureRecognizer!)
    return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
    view.removeGestureRecognizer(tapGestureRecognizer!)
    return true
}

当用户触摸self.view时:

func handleTap(recognizer: UITapGestureRecognizer) {
    sampleSearchBar.endEditing(true)
    sampleSearchBar.resignFirstResponder()
}

0

迅速,2020年5月5日。

我有一个textField和一个tableView,当我输入文本时,该视图便可见。

初始状态 因此,当我点击tableViewCell或其他东西时,我想要2个不同的事件。

正在显示Keyboard和tableView

首先,我们添加tapGestureRecognizer。

tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
tap.delegate = self
view.addGestureRecognizer(tap)

@objc func viewTapped() {
        view.endEditing(true)
}

然后,将以下检查添加到UIGestureRecognizerDelegate中:

extension StadtViewController: UIGestureRecognizerDelegate {

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view?.isDescendant(of: self.tableView) == true {
        return false
    } else {
        view.endEditing(true)
        return true
    }
}

}

如果我想先隐藏键盘,则tableView仍然可见并且对我的点击响应。

在此处输入图片说明


-1

这是基于上述答案的解决方案...对我有用...

//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];

- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
    //Write your code here
}

-1

问题:就我而言,问题是我最初在每个collectionView单元格中放置了一个按钮,并设置了填充该单元格的约束,因此当单击该单元格时,它将单击该按钮,但是按钮功能为空,因此没有任何作用似乎正在发生。

FIX:我通过从集合视图单元格中删除按钮来解决此问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.