UITableView 上的触摸事件?

29

我在视图中添加了UIViewControllerUITableView,现在我想要实现的是当我点击任意一行时,在底部显示一个视图。如果用户点击除行或底部视图以外的其他地方,我想要隐藏该视图。

问题是当我在UITableView上点击时,它不会触发touchesEnded事件。

那么如何检测UITableView上的触摸事件并将其与选定行事件区分开呢?

谢谢。


我不确定我理解了。您的意思是,您有一个包含UITableView的UIView(viewA),以及另一个UIView(viewB)(在底部)。如果用户触摸除UiTableViewCell或viewB之外的任何地方,则希望隐藏viewB。否则,显示viewB。是这样吗? - Jason Cragun
是的,我就是想要这样做。如果用户点击了UITableViewCell,我想要显示viewB。这可以通过didRowSelectedAtIndexPath:方法来实现。 - Kapil Choubisa
那么你是在问:“如何捕获发生在UITableView上但不在单元格中的触摸事件?” - Jason Cragun
当然可以。我需要检测UITableView的触摸,而不是单元格。 - Kapil Choubisa
7个回答

54

无需子类化任何内容,您可以向 UITableView 添加 UITapGestureRecognizer,并根据您的标准吸收手势或不吸收手势。

在您的 viewDidLoad 中:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnTableView:)];
[self.myTableView addGestureRecognizer:tap];

然后,根据这个条件实现你的操作:

-(void) didTapOnTableView:(UIGestureRecognizer*) recognizer {
    CGPoint tapLocation = [recognizer locationInView:self.myTableView];
    NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:tapLocation];

    if (indexPath) { //we are in a tableview cell, let the gesture be handled by the view
        recognizer.cancelsTouchesInView = NO;
    } else { // anywhere else, do what is needed for your case
        [self.navigationController popViewControllerAnimated:YES];
    }
}

请注意,如果您只想在表格上简单地捕获点击事件,而不是在单元格行中的任何按钮上捕获,那么您只需要使用上面的第一个代码片段。一个典型的例子是当您有一个UITableView并且还有一个UISearchBar时。您希望在用户点击、滚动等操作表视图时消除搜索栏。代码示例...
-(void)viewDidLoad {
    [super viewDidLoad];
    etc ...

    [self _prepareTable];
}
-(void)_prepareTable {
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.tableView.allowsSelection = NO;
    etc...

    UITapGestureRecognizer *anyTouch =
        [[UITapGestureRecognizer alloc]
         initWithTarget:self action:@selector(tableTap)];
    [self.tableView addGestureRecognizer:anyTouch];
}
// Always drop the keyboard when the user taps on the table:
// This will correctly NOT affect any buttons in cell rows:
-(void)tableTap {
    [self.searchBar resignFirstResponder];
}
// You probably also want to drop the keyboard when the user is
// scrolling around looking at the table. If so:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self.searchBar resignFirstResponder];
}
// Finally you may or may not want to drop the keyboard when
// a button in one cell row is clicked. If so:
-(void)clickedSomeCellButton... {
    [self.searchBar resignFirstResponder];
    ...
}

希望它能帮助到某人。

1
我更喜欢在scrollViewWillBeginDragging:(UIScrollView *)scrollView方法中调用resignFirstResponder,而不是在scrollViewDidScroll方法中调用。因为如果用户在滚动减速期间滚动并单击searchBar - 键盘将在屏幕上出现一秒钟,然后它将隐藏,这不是适当的行为。使用scrollViewWillBeginDragging方法可以解决这个问题。 - Werewolf

14
你应该将触摸事件转发到视图的控制器上。 继承你的表格视图控件,然后重写这个方法:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];  //let the tableview handle cell selection 
    [self.nextResponder touchesBegan:touches withEvent:event]; // give the controller a chance for handling touch events 
}

那么,您可以在控制器的touch方法中实现想要的操作。


谢谢@longway,但是我没有收到touchesBegan的调用,那么我该如何将它转发给视图控制器呢? 这个问题主要是关于当我在tableView上触摸时如何调用touchesBegan事件 :) - Kapil Choubisa
你可以创建一个自定义的UITableView子类,重写触摸处理方法。然后使用你的自定义tableview类来创建你的表格。 - longway
嗨,我尝试了你的解决方案,当然可以检测到tableview上的触摸并传递给其相应的superview。但现在我的问题是UITableview委托方法,即didSelectRowAtIndexPath没有被调用。最终我想做的是先检测tableview上的触摸,将其传递给superview执行一些操作,然后再执行didSelectRowAtIndexPath。你能帮我吗? - Kavya Indi

8

我刚刚偶然发现了可能解决你问题的方法。在创建表格视图时,请使用以下代码:

tableView.canCancelContentTouches = NO;

如果不将其设置为NO,则只要表格视图出现轻微的垂直移动(如果在代码中使用NSLog语句,则会发现一旦表格开始垂直滚动,touchesCancelled就会被调用),触摸事件就会被取消。


2
你的解决方案不起作用。我尝试了你的解决方案,并使用了所有的触摸事件和日志,包括 touchesCancelled。在 viewDidLoad 中,我设置了 [tblTemp setCanCancelContentTouches:NO];。但是我的任何触摸方法都没有被调用。 - Kapil Choubisa

6

我长时间以来一直面临这个问题,没有找到任何可行的解决方案。最终我选择了另一种方法。我知道从技术上讲,这不是解决方案,但这肯定可以帮助寻找同样问题答案的人。

在我的情况下,我想选择一行,然后在点击表格或视图的任何位置后显示一些选项,我想隐藏那些选项或执行除之前选择的行之外的任何任务。为此,我进行了以下操作:

  1. Set touch events for the view. This will do the task when you touch anywhere on the view except the table view.

  2. TableView's didSelectRowAtIndexPath do following

     - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
        if(indexPath.row != previousSelectedRow && previousSelectedRow != -1) {
            // hide options or whatever you want to do
            previousSelectedRow = -1;
        }
        else {
            // show your options or other things
            previousSelectedRow = indexPath.row;
        }
     }
    

我知道这是旧帖子,不是一个好的技术解决方案,但是这对我起作用。我发布这个答案是因为这肯定会帮助到某些人。

注意:此处编写的代码可能有拼写错误,因为是直接输入的。 :)


3
尝试以下方法:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{

}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{

}

使用scrollViewDidEndDragging作为touchesEnded的替代方法。希望这有所帮助。

3

要在 UITableView 上接收触摸事件,请使用以下代码:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  //<my stuff>

  [super touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   //<my stuff>

   [super touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
  //<my stuff>

  [super touchesEnded:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
   //<my stuff>
   [super touchesCancelled:touches withEvent:event];
}

-3
在您的控制器类中声明一个方法,该方法可以删除底部视图。代码示例如下:
-(IBAction)bottomViewRemove:(id)sender {

[bottomView removeFromSuperview];

}

在Interface Builder中,选择您的视图,在身份检查器中的自定义类部分中,将类从UIView更改为UIControl。之后进入连接检查器,并将TouchUpInside事件连接到上面声明的方法。希望这可以帮助您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接