UITapGestureRecognizer 中断 UITableView didSelectRowAtIndexPath
Posted
技术标签:
【中文标题】UITapGestureRecognizer 中断 UITableView didSelectRowAtIndexPath【英文标题】:UITapGestureRecognizer breaks UITableView didSelectRowAtIndexPath 【发布时间】:2011-11-19 06:58:44 【问题描述】:我已经编写了自己的函数来在键盘出现时向上滚动文本字段。为了通过点击离开文本字段来关闭键盘,我创建了一个UITapGestureRecognizer
,它负责在点击离开时在文本字段上退出第一响应者。
现在我还为文本字段创建了一个自动完成功能,它在文本字段下方创建一个UITableView
,并在用户输入文本时用项目填充它。
didSelectRowAtIndexPath
不会被调用。相反,似乎点击手势识别器被调用并且只是让第一响应者辞职。
我猜有某种方法可以告诉点击手势识别器继续将点击消息传递给UITableView
,但我不知道它是什么。任何帮助将不胜感激。
【问题讨论】:
见this thread。 【参考方案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】:我认为没有必要写代码块只是简单地设置
cancelsTouchesInView
到 false
用于您的手势对象,
默认情况下它是 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 文件中实现此 <UIGestureRecognizerDelegate>
。
![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 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
【讨论】:
【参考方案19】:在swift中你可以在里面使用这个
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
if CheckTheTime() == true
// do something
else
func CheckTheTime() -> Bool
return true
【讨论】:
【参考方案20】:这是我基于上述答案的解决方案... 它对我有用...
//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
【讨论】:
【参考方案21】:问题: 就我而言,问题是我最初在每个 collectionView 单元格中放置了一个按钮并设置约束以填充单元格,以便在单击单元格时它会单击按钮,但是按钮功能是空的,所以什么都没有出现正在发生。
修复: 我通过从集合视图单元格中删除按钮来解决此问题。
【讨论】:
以上是关于UITapGestureRecognizer 中断 UITableView didSelectRowAtIndexPath的主要内容,如果未能解决你的问题,请参考以下文章
UITapGestureRecognizer 不适用于 UIView 动画
使用 UITapGestureRecognizer 无法识别的选择器
UIlabel 中的 UITapGestureRecognizer 错误