UITableView点击取消选择单元格

27

我有一个UITableViewCell,在被轻击时会被选中。在这个选择状态下,如果用户再次点击单元格,我希望取消选中该单元格。

我找不到任何委托调用被触发。有什么实现这一功能的想法吗?是真的需要一个手势识别器吗?

14个回答

39

你可以使用委托方法 willSelectRowAtIndexPath: 来实现此功能。

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if ([cell isSelected]) {
        // Deselect manually.
        [tableView.delegate tableView:tableView willDeselectRowAtIndexPath:indexPath];
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        [tableView.delegate tableView:tableView didDeselectRowAtIndexPath:indexPath];

        return nil;
    }

    return indexPath;
}
请注意,deselectRowAtIndexPath: 不会自动调用委托方法,因此您需要手动进行这些调用。

9
在调用委托对象的方法之前,应始终检查委托对象是否响应该选择器。 - Patrick

30

在 Swift 4 中:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {

    if let indexPathForSelectedRow = tableView.indexPathForSelectedRow,
        indexPathForSelectedRow == indexPath {
        tableView.deselectRow(at: indexPath, animated: false)
        return nil
    }
    return indexPath
}

需要一个“override”,如果您不手动调用委托方法(willDeselect...和didDeselect...),可能会令人困惑。 - snakeoil

6
使用“多选”模式时,当您单击已选择的行时,“didSelectRowAtIndexPath”不会被调用。在“单选”模式下,可以通过编程方式选择多个行,所有点击都将触发didSelectRowAtIndexPath事件。
这只是提醒每个遇到相同问题的人。

4

保持单选模式,但按以下方式覆盖函数:

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    if tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false {
       tableView.deselectRow(at: indexPath, animated: true)
       return nil
    }

    return indexPath
}

override func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? {
    return nil
}

这个代码完美运行,应该被接受为答案。需要注意的是,如上所述,Tableview 应该设置为单选模式。 - Raymond

4

这里有一个更加简洁的解决方案:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([[tableView indexPathForSelectedRow] isEqual:indexPath]) {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        return nil;
    }
    return indexPath;
}

不要使用“==”来比较对象。应该是这样的if([[tableView indexPathForSelectedRow] isEqual:indexPath]){ - ahwulf

3
当您选择一个单元格时,这个代理方法就会被触发。
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{


        UITableViewCell *theUITableViewCell = [tableView cellForRowAtIndexPath:path];
        if (theUITableViewCell.selected == YES){
            self.tableView deselectRowAtIndexPath:<#(NSIndexPath *)#> animated:<#(BOOL)#>
            // Do more thing
        }else{
            // By default, it is highlighted for selected state
        }

 }

这是伪代码。


3
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if ([cell isSelected]) {
        // Deselect manually.
        if ([tableView.delegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)]) {
            [tableView.delegate tableView:tableView willDeselectRowAtIndexPath:indexPath];
        }
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        if ([tableView.delegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)]) {
            [tableView.delegate tableView:tableView didDeselectRowAtIndexPath:indexPath];
        }
        return nil;
    }

    return indexPath;
}

3
我认为在切换时使用蓝色选择颜色可能会给人留下奇怪的印象。为什么不使用像复选标记这样的附件?这是一种更为常见的选择切换技术(无论是多个单元格还是单个单元格)。
请参阅此答案以获取信息: UITableView 多重选择

2
你可以尝试这个,它对我有效(适用于单选或多选):
A. 在viewDidLoad中分配一个私有的NSMutableArray,例如:
@interface XXXViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
    NSMutableArray * selectedZoneCellIndexPaths;
}


- (void)viewDidLoad
{
    selectedZoneCellIndexPaths = [[NSMutableArray alloc] init];
}

在UITableViewDelegate的didSelectRow方法中添加以下代码:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

     for (NSIndexPath * iter in selectedZoneCellIndexPaths)
     {
         if (indexPath.section == iter.section && indexPath.row == iter.row)
         {
             [tableView deselectRowAtIndexPath:indexPath animated:YES];
             [selectedZoneCellIndexPaths removeObject:iter];
             return;
         }
     }
     [selectedZoneCellIndexPaths addObject:indexPath];
     return;
}

如果你没有正确地处理单元格的出列操作,这将导致错误。 - Paul de Lange

1

prisonerjohn的答案的Swift版本:

public func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
  let cell = tableView.cellForRowAtIndexPath(indexPath)
  if (cell?.selected ?? false) {
    // Deselect manually.
    tableView.delegate!.tableView?(tableView, willDeselectRowAtIndexPath: indexPath)
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
    tableView.delegate!.tableView?(tableView, didDeselectRowAtIndexPath: indexPath)
    return nil
  }
return indexPath;
}

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