UITapGestureRecognizer中断了UITableView didSelectRowAtIndexPath

时间:2011-11-19 06:58:44

标签: ios uitableview uitapgesturerecognizer

我已经编写了自己的函数来在键盘出现时向上滚动文本字段。为了通过点击文本字段来关闭键盘,我创建了一个UITapGestureRecognizer,用于在点击时关注文本字段上的第一响应者。

现在我还为文本字段创建了一个自动填充功能,在文本字段正下方创建UITableView,并在用户输入文字时用项目填充它。

但是,当选择自动完成表中的一个条目时,didSelectRowAtIndexPath不会被调用。相反,看起来轻敲手势识别器正在被调用并且只是辞职第一响应者。

我猜是有一种方法可以告诉点击手势识别器继续将点击消息传递到UITableView,但我无法弄清楚它是什么。任何帮助都将非常感激。

19 个答案:

答案 0 :(得分:246)

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

解决方案是实施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;
}

照顾它。希望这也有助于其他人。

答案 1 :(得分:212)

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

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

这使UIGestureRecognizer识别出点按,并将触摸传递给下一个响应者。此方法的意外后果是,如果您在屏幕上显示UITableViewCell,则会推送另一个视图控制器。如果用户点击该行以关闭键盘,则将识别键盘和推送。我怀疑这是你的意图,但这种方法适用于很多情况。

另外,扩展罗伯特的答案,如果你有一个指向相关的tableview的指针,那么你可以直接比较它的类而不必转换为字符串,并希望Apple不会改变命名法:

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

    return //YES/NO

}

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

答案 2 :(得分:63)

将识别器的cancelsTouchesInView设置为false。否则,消费"消费"触摸它自己,并没有将它传递到表视图。这就是选择事件永远不会发生的原因。

例如在swift

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

答案 3 :(得分:34)

对于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
}

答案 4 :(得分:17)

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

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

我只是在用户点击的屏幕上寻找一个单元格。如果没有找到索引路径,那么我让手势接收到触摸,否则我取消它。对我来说它很棒。

答案 5 :(得分:16)

我认为没有必要只是简单地设置代码块     您的手势对象cancelsTouchesInViewfalse, 默认情况下它是true,您只需将其设置为false即可。UITapGesture。 如果您在代码中使用UIScrollView对象并使用false(tableview,collectionview),请设置此属性let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard)) tap.cancelsTouchesInView = false view.addGestureRecognizer(tap)

transaction.interfaces.NoTransaction

答案 6 :(得分:14)

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

这也有一个好处,它适用于单个视图上的多个表(无需添加额外的代码)。

警告:假设Apple不会更改类名。

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

答案 7 :(得分: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()
        ...
    }
    ...
}

答案 8 :(得分:3)

迅速2020年5月5日。

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

Initial state 因此,当我点击tableViewCell或其他东西时,我想要两个不同的事件。

Keyboard and tableView are being shown

首先,我们添加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仍然可见并且对我的点击有反应。

enter image description here

答案 9 :(得分:1)

简单的解决方案是在UITableViewCell中使用UIControl实例来获取触摸。您可以使用userInteractionEnables == NO将任何视图添加到UIControl以获取点击。

答案 10 :(得分:1)

虽然已经很晚了,很多人发现上述建议工作正常,但我无法让Jason或TMilligan的方法工作。

我有一个静态 tableView,其中多个单元格包含仅使用数字键盘接收数字输入的textFields。这对我来说很理想:

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

确保您已在.h文件中实施此<UIGestureRecognizerDelegate>

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

答案 11 :(得分:1)

只需将cancels touches in view下的任何手势的false设置为(table/collection)View

-Interfacebuilder

Interfacebuilder

-代码

<#gestureRecognizer#>.cancelsTouchesInView = false

答案 12 :(得分:1)

对于 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方法正常工作。

答案 13 :(得分:0)

在swift中你可以在里面使用它

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if CheckTheTime() == true {
        // do something 
    }else{
    }
}

func CheckTheTime() -> Bool{
    return true
}

答案 14 :(得分:0)

我的情况不同,包括uisearchbar和self.view上的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()
}

答案 15 :(得分: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;
}

答案 16 :(得分: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

你可能想要更新键盘监听器的单例模式,我还没有达到它。希望这适用于其他所有人,也适用于我。 ^^

答案 17 :(得分:-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
}

答案 18 :(得分:-1)

ISSUE: 在我的例子中,问题是我最初在每个collectionView单元格中放置一个按钮并设置约束来填充单元格,这样当单击单元格时它会单击按钮,但是按钮功能是空的,所以没有任何东西出现在正在发生。

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