225

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

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

但是,在选择自动完成表中的一个条目时,didSelectRowAtIndexPath不会被调用。相反,似乎点击手势识别器被调用并且只是辞去第一响应者。

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

4

21 回答 21

276

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

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

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

那照顾了它。希望这对其他人也有帮助。

于 2011-11-20T00:30:58.983 回答
227

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

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

这可以让UIGestureRecognizer识别器识别水龙头并将触摸传递给下一个响应者。这种方法的一个意想不到的后果是,如果您有一个UITableViewCell推送另一个视图控制器的屏幕。如果用户点击该行以关闭键盘,则键盘和按下都会被识别。我怀疑这是您的意图,但这种方法适用于许多情况。

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

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

    return //YES/NO

}

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

于 2012-10-12T03:28:23.463 回答
83

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

例如在swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)
于 2016-03-22T09:39:37.643 回答
46

对于 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
}
于 2015-10-22T08:39:10.007 回答
28

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

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

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

于 2014-11-28T10:46:11.263 回答
20

我认为没有必要编写代码块,只需简单地设置 cancelsTouchesInViewfalse您的手势对象,默认情况下它是true,您只需设置它false。如果您UITapGesture在代码中使用对象并且还使用UIScrollView(tableview,collectionview)然后设置此属性false

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
于 2018-01-15T10:04:01.987 回答
14

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

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

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

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}
于 2012-08-13T16:48:35.933 回答
10

对于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表格视图的方法正常工作。

于 2019-07-13T11:38:15.187 回答
6

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

生成的代码是 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()
        ...
    }
    ...
}
于 2018-11-12T20:48:16.973 回答
6

只需设置cancels touches in viewfalse下面的任何手势(table/collection)View

- 界面构建器

界面构建器

- 代码

<#gestureRecognizer#>.cancelsTouchesInView = false
于 2019-04-27T08:03:16.100 回答
6

斯威夫特 5,2020 年 5 月。

我有一个 textField 和一个 tableView 在我输入文本时变得可见。

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

正在显示键盘和 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 仍然可见并响应我的点击。

在此处输入图像描述

于 2020-05-08T22:46:26.493 回答
1

虽然为时已晚,许多人发现上述建议效果很好,但我无法让 Jason 或 TMilligan 的方法发挥作用。

我有一个静态表视图,其中包含多个单元格,其中包含仅使用数字键盘接收数字输入的文本字段。这对我来说是理想的:

- (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 并关闭了任何活动键盘。

于 2015-02-28T03:54:53.367 回答
1

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

于 2015-03-10T13:25:01.467 回答
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

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

于 2014-03-23T17:12:01.787 回答
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;
}
于 2014-05-02T04:08:53.247 回答
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()
}
于 2016-06-01T06:17:22.620 回答
0

Swift 解决方案,于 2021 年生效。对于此解决方案,您不必拥有对表视图的引用。

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    //return !(touch.view is UITableViewCell) <-- doesn't work, the type of the touched class is not UITableViewCell anymore
    
    var v = touch.view
    while v != nil {
        if v is UITableView { return false }
        v = v?.superview
    }
    return true
}

用法:

let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YOUR_METHOD))
gestureRecognizer.delegate = self
YOUR_VIEW.addGestureRecognizer(gestureRecognizer)
于 2021-09-01T18:37:15.607 回答
0

对于 Swift 5 中的 CollectionView:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.location(in: mCollectionView)
        return (mCollectionView.indexPathForItem(at: location) == nil)
    }
    return true
}
于 2022-02-16T20:57:22.897 回答
-1

迅速你可以在里面使用它

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

func CheckTheTime() -> Bool{
    return true
}
于 2016-03-20T13:55:14.753 回答
-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
}
于 2018-04-12T06:31:26.723 回答
-1

问题:在我的情况下,问题是我最初在每个 collectionView 单元格中放置了一个按钮并设置约束以填充单元格,以便在单击单元格时它会单击按钮,但是按钮功能是空的,所以什么都没有似乎正在发生。

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

于 2018-04-14T11:26:24.613 回答