ios UITapGestureRecognizer 打破了 UITableView didSelectRowAtIndexPath
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8192480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UITapGestureRecognizer breaks UITableView didSelectRowAtIndexPath
提问by Jason
I have written my own function to scroll text fields up when the keyboard shows up. In order to dismiss the keyboard by tapping away from the text field, I've created a UITapGestureRecognizer
that takes care of resigning first responder on the text field when tapping away.
我已经编写了自己的函数来在键盘出现时向上滚动文本字段。为了通过点击远离文本字段来关闭键盘,我创建了一个UITapGestureRecognizer
负责在点击离开时退出文本字段上的第一响应者。
Now I've also created an autocomplete for the textfield that creates a UITableView
just below the text field and populates it with items as the user enters text.
现在,我还为UITableView
文本字段创建了一个自动完成功能,它会在文本字段下方创建一个并在用户输入文本时使用项目填充它。
However, when selecting one of the entries in the auto completed table, didSelectRowAtIndexPath
does not get called. Instead, it seems that the tap gesture recognizer is getting called and just resigns first responder.
但是,在选择自动完成表中的其中一个条目时,didSelectRowAtIndexPath
不会被调用。相反,似乎点击手势识别器正在被调用并且只是让第一响应者辞职。
I'm guessing there's some way to tell the tap gesture recognizer to keep passing the tap message on down to the UITableView
, but I can't figure out what it is. Any help would be very appreciated.
我猜有一些方法可以告诉点击手势识别器继续将点击消息传递给UITableView
,但我不知道它是什么。任何帮助将不胜感激。
回答by Jason
Ok, finally found it after some searching through gesture recognizer docs.
好的,在搜索手势识别器文档后终于找到了它。
The solution was to implement UIGestureRecognizerDelegate
and add the following:
解决方案是实施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;
}
That took care of it. Hopefully this will help others as well.
那照顾它。希望这也能帮助其他人。
回答by TMilligan
The easiest way to solve this problem is to:
解决此问题的最简单方法是:
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];
This lets the UIGestureRecognizer
recognize the tap and also pass the touch to the next responder. An unintended consequence of this method is if you have a UITableViewCell
on-screen that pushes another view controller. If the user taps the row to dismiss the keyboard, both the keyboard and the push will be recognized. I doubt this is what you intend, but this method is adequate for many situations.
这可以让UIGestureRecognizer
识别点击并将触摸传递给下一个响应者。此方法的一个意外后果是,如果您有一个UITableViewCell
推送另一个视图控制器的屏幕。如果用户点击该行以关闭键盘,则键盘和推动都将被识别。我怀疑这是否是您的意图,但这种方法适用于许多情况。
Also, expanding on Robert's answer, if you have a pointer to the tableview in question, then you can directly compare its class instead of having to convert to a string and hope Apple doesn't change the nomenclature:
此外,扩展罗伯特的回答,如果您有一个指向相关表视图的指针,那么您可以直接比较它的类,而不必转换为字符串,并希望 Apple 不会更改命名法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if([touch.view class] == tableview.class){
return //YES/NO
}
return //YES/NO
}
Remember, you must also declare the UIGestureRecognizer
to have a delegate with this code in it.
请记住,您还必须声明UIGestureRecognizer
具有包含此代码的委托。
回答by Abdulrahman Masoud
Set cancelsTouchesInView
of your recognizer to false. Otherwise, it "consumes" the touch for itself, and does not pass it on to the table view. That's why the selection event never happens.
将cancelsTouchesInView
您的识别器设置为 false。否则,它会为自己“消耗”触摸,并且不会将其传递给表格视图。这就是选择事件永远不会发生的原因。
for example in swift
例如在 swift
let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)
回答by Nikolaj Nielsen
And for Swift (based on answer from @Jason):
对于 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
}
回答by bonokite
I may have a better solution to add a tap gesture over a table view but allowing cell selection at the same time:
我可能有更好的解决方案来在表格视图上添加点击手势,但同时允许选择单元格:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if gestureRecognizer is UITapGestureRecognizer {
let location = touch.locationInView(tableView)
return (tableView.indexPathForRowAtPoint(location) == nil)
}
return true
}
I just look for a cell at the point of the screen where the user is tapping. If no index path is found then I let the gesture receive the touch otherwise I cancel it. For me it works great.
我只是在用户点击的屏幕上寻找一个单元格。如果没有找到索引路径,那么我让手势接收触摸,否则我取消它。对我来说效果很好。
回答by SHUBHAYAN KUNDU
I think there is no need to write blocks of codes just simply set
cancelsTouchesInView
to false
for your gesture object ,
by default it's true
and you just have to set it false
.
If you are using UITapGesture
object in your code and also using UIScrollView
(tableview , collectionview)then set this property false
我觉得没有必要只是单纯的若干组代码写入块
cancelsTouchesInView
到false
你的姿势对象,默认情况下它true
,你只需要设置它false
。如果您UITapGesture
在代码中使用object 并且还使用UIScrollView
(tableview , collectionview) 然后设置此属性false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
回答by Robert Altman
A similar solution is to implement gestureRecognizer:shouldReceiveTouch:
using the view's class to determine what action to take. This approach has the advantage of not masking taps in the region directly surrounding the table (these area's views still descend from the UITableView instances, but they do not represent cells).
一个类似的解决方案是gestureRecognizer:shouldReceiveTouch:
使用视图的类来确定要执行的操作。这种方法的优点是不屏蔽直接围绕表格的区域中的点击(这些区域的视图仍然来自 UITableView 实例,但它们不代表单元格)。
This also has a bonus that it works with multiple tables on a single view (without adding extra code).
这还有一个好处,它可以在单个视图上处理多个表(无需添加额外代码)。
Caveat: there is an assumption that Apple won't change the classname.
警告:假设 Apple 不会更改类名。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}
回答by Ashish Kanani
For Swift 4.2the solution was to implement UIGestureRecognizerDelegate
and add the following:
对于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
}
}
when you click on table view this delegate method returns false and didSelectRowAtIndexPath
method of table view working properly.
当您单击表视图时,此委托方法返回 false 并且didSelectRowAtIndexPath
表视图的方法正常工作。
回答by Ali Mizan
I had a different situation where I wanted the touch gesture function to be called onlywhen the user tapped outsideof the table view. If the user tapped inside the table view, then the touch gesture function shouldn't be called. Additionally, If the touch gesture function is called, it should still pass the touch event to the view that was tapped on rather than consuming it.
我有一个不同的情况,我希望只有当用户在表格视图之外点击时才调用触摸手势功能。如果用户在 table view 内部点击,则不应调用触摸手势功能。此外,如果触摸手势功能被调用,它仍然应该将触摸事件传递给被点击的视图而不是消费它。
The resulting code is a combination of Abdulrahman Masoud's answer, and Nikolaj Nielsen's answer.
生成的代码是 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")
}
}
And in the MyViewController
class, the class which has the UITableView
, in the onViewDidLoad()
, I made sure to call addGestureRecognizer()
:
在MyViewController
课堂上,有UITableView
, 在中的课程,onViewDidLoad()
我确保调用addGestureRecognizer()
:
class MyViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
...
self.addGestureRecognizer()
...
}
...
}