【问题标题】:UITapGestureRecognizer breaks UITableView didSelectRowAtIndexPathUITapGestureRecognizer 中断 UITableView didSelectRowAtIndexPath
【发布时间】:2012-01-01 19:27:36
【问题描述】:

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

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

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

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

【问题讨论】:

标签: ios uitableview uitapgesturerecognizer


【解决方案1】:

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

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

这就解决了。希望这对其他人也有帮助。

【讨论】:

  • 您可能还需要跳过对文本字段的点击。
  • return ![touch.view isKindOfClass:[UITableViewCell class]]; 让我更幸运
  • @Josh 如果您点击单元格内的标签,这将不起作用。实际上,杰森的答案非常适合我的需要。
  • 但这不是您问题的答案,不是吗?这是一个非此即彼的选择。基本上,手势识别器对表格视图的任何触摸都无关紧要......您想要的是手势识别器和表格视图选择都可以工作
  • @Durgaprasad 要在选择单元格时明确禁用手势识别器,我在[touch.view isDescendantOfView: self.tableView] 之前添加了![touch.view isEqual: self.tableView] && 以排除tableView 本身(因为isDescendantOfView: 也如果参数视图是接收器视图本身,则返回YES)。希望对其他想要同样结果的人有所帮助。
【解决方案2】:

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

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 才能拥有包含此代码的委托。

【讨论】:

  • 谢谢,setCancelsTouchesInView 改变了一切 :)
  • 可能仍然想使用isDescendantOfView 而不是仅仅检查class 是否相等,因为您可能正在点击子视图。取决于你需要什么。
【解决方案3】:

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

例如swift

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

【讨论】:

  • 非常感谢!
  • 这是 Swift 中最干净的。谢谢
  • 在tableview的单元格中效果很好,可爱又简单!
  • @laoyur 非常真实
  • 仍然是单击单元格而不是点击手势
【解决方案4】:

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

【讨论】:

  • 非常感谢这节省了我的时间。
【解决方案5】:

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

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

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

【讨论】:

  • 这个解决方案太棒了!将单元格与表格的空字段区分开来非常有用..
  • Swift 5: let location = touch.location(in: tableView)
  • 工作就像一个魅力。你就是男人。
【解决方案6】:

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

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

【讨论】:

  • 感谢您发布此答案
【解决方案7】:

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

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

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

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

【讨论】:

  • 消除类名字符串,使用这一行return ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
【解决方案8】:

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

【讨论】:

  • 感谢好友发布此答案,您节省了我的时间。
【解决方案9】:

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

生成的代码是 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()
        ...
    }
    ...
}

【讨论】:

    【解决方案10】:

    只需将cancels touches in view 设置为false 即可用于(table/collection)View 下方的任何手势:

    - 界面生成器

    - 代码

    <#gestureRecognizer#>.cancelsTouchesInView = false
    

    【讨论】:

    • 如果我想禁用它的工作不仅允许 didSelectRowAt 使用它怎么办?
    【解决方案11】:

    Swift 5,2020 年 5 月。

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

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

    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

    【讨论】:

      【解决方案12】:

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

      我有一个 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;
      }
      

      确保您已在 .h 文件中实现此 &lt;UIGestureRecognizerDelegate&gt;

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

      【讨论】:

        【解决方案13】:

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

        【讨论】:

          【解决方案14】:

          这是我的解决方案,它将识别器的 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
          

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

          【讨论】:

            【解决方案15】:

            为 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】:

              我的情况不同,包括 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()
              }
              

              【讨论】:

                【解决方案17】:

                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)
                

                【讨论】:

                  【解决方案18】:

                  在swift中你可以在里面使用这个

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

                  【讨论】:

                    【解决方案19】:

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

                    //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
                    }
                    

                    【讨论】:

                      【解决方案20】:

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

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

                      【讨论】:

                        猜你喜欢
                        • 2012-02-18
                        • 1970-01-01
                        • 2011-11-17
                        • 2017-01-26
                        • 1970-01-01
                        • 2012-01-14
                        • 2014-12-22
                        • 1970-01-01
                        相关资源
                        最近更新 更多