UITableView 多选

Posted

技术标签:

【中文标题】UITableView 多选【英文标题】:UITableView Multiple Selection 【发布时间】:2010-06-14 21:02:20 【问题描述】:

如何将 UITableView 添加到我的基于视图的应用程序中,在该应用程序中,用户将点击多个单元格,它将被选中,就像时钟应用程序的“新闹钟”设置名为“重复”(时钟>闹钟> + >Repeat),我怎样才能得到一个数组中所有选定的单元格?

【问题讨论】:

嘘。我没有看到苹果在这方面的例子。 【参考方案1】:

多选请在viewDidLoad()中添加下面一行

tableView.allowsMultipleSelection = true

tableView(_:cellForRowAt:) 中将每个cell 出列(或初始化)后配置它

let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
// cell.accessoryView.hidden = !rowIsSelected // if using a custom image

当每个单元格被选中/取消选中时更新它

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = .checkmark
    // cell.accessoryView.hidden = false // if using a custom image


override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) 
    let cell = tableView.cellForRow(at: indexPath)!
    cell.accessoryType = .none
    // cell.accessoryView.hidden = true  // if using a custom image

完成后,获取所有选定行的数组

let selectedRows = tableView.indexPathsForSelectedRows

并获取选定的数据,其中dataArray 映射到只有 1 个部分的表视图的行

let selectedData = selectedRows?.map  dataArray[$0.row].ID 

【讨论】:

我不明白为什么这不是公认的答案...如果您使用自定义单元格,您还可以根据单元格的状态更改单元格的 setSelected:animated: 方法的外观跨度> 太棒了!奇迹般有效。这个问题的最佳答案。欣赏! 获取选定对象的索引 NSArray *selectedCells = [self.tableView indexPathsForSelectedRows]; 这样的帖子让 *** 如此出色!简洁明了,干得好! 有史以来最简洁的答案。【参考方案2】:

-tableView:didSelectRowAtIndexPath: 的实现中,您将根据当前值设置表格视图单元格的accessoryType 属性(因此它可以通过多次点击来打开和关闭)。例如:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)path 
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:path];

    if (cell.accessoryType == UITableViewCellAccessoryCheckmark) 
        cell.accessoryType = UITableViewCellAccessoryNone;
     else 
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    

除了单元格自己的辅助类型状态之外,您还可以维护一个选定状态数组,或者遍历表视图中的单元格以查询每个单元格的状态以读出选定的行。

【讨论】:

This won't work -- indexPath refers to the wrong cell when a cell is selected and you scroll.结果,您将看到多个项目被选中。 与上一条评论一样,这不起作用,因为您将在滚动时选择多个项目。 -1 这个解决方案有一个问题,一旦你选择了某些行并滚动,你会看到许多你尚未选择的单元格的复选标记。 此解决方案在取消选中单元格时还有另一个问题,您必须点击两次才能使附件类型为无,因为当我们选择一个单元格时,它会使其附件类型复选标记并将其状态更改为选中。所以当你想取消选中时,它不会去这个方法didSelectRowAtIndexPath而是去didDeselectRowAtIndexPath。所以你也必须实现这个方法。 要解决重新加载问题,请在 cellForRowAtIndexPath 中执行此操作: if(cell.isSelected) cell.accessoryType = UITableViewCellAccessoryCheckmark; else cell.accessoryType = UITableViewCellAccessoryNone; 【参考方案3】:

@BrendanBreg implementation 对我不起作用。 @RaphaelOliveira 提供了 good solution,但是当您向下滚动表格时 - 选择了错误的行(因为 UITableView 缓存了它的单元格)。所以,我稍微修改了 Raphael 的解决方案:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;


- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;


/*Here is modified part*/

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    /*
    ...
    Your implementation stays here
    we're just adding few lines to make sure
    that only correct rows will be selected
    */

    if([[tableView indexPathsForSelectedRows] containsObject:indexPath]) 
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
     else 
        cell.accessoryType = UITableViewCellAccessoryNone;
    

【讨论】:

滚动时隐藏选定的行!【参考方案4】:
self.tableView.allowsMultipleSelection = YES;

【讨论】:

【参考方案5】:

除了上面的好答案之外,只是一个快速提示:要从时钟应用程序中模仿 Apple 的风格(使行选择颜色在选中/取消选中该行后淡出),将其添加到 didSelectRowAtIndexPath,在条件:

[self.tableView deselectRowAtIndexPath:indexPath animated:YES];

来自 Apple 的 TableMultiSelect 指南。

【讨论】:

【参考方案6】:

这就是你需要的 http://developer.apple.com/library/ios/#samplecode/TableMultiSelect/Introduction/Intro.html#//apple_ref/doc/uid/DTS40011189-Intro-DontLinkElementID_2

【讨论】:

【参考方案7】:

我在很多开发人员中都看到过这个问题。由于表格视图重用单元格的性质,它会删除或随意进行检查。我为此创建了一个可行的解决方案。从DevelopmentSupportWorkspace 克隆/下载代码并从那里执行UITableViewTest 项目。

这里是代码摘要:

@interface CheckBoxTestTableViewController ()

@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, strong) NSMutableDictionary *selectedIndexDictionary;

@end

@implementation CheckBoxTestTableViewController

- (void)viewDidLoad 
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    //
    _dataArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImageList" ofType:@"plist"]];
    _selectedIndexDictionary = [NSMutableDictionary dictionary];


- (void)didReceiveMemoryWarning 
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    return _dataArray.count;



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"checkMarkCell" forIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;

    // Configure the cell...
    cell.textLabel.text = _dataArray[indexPath.row][@"text"];
    if (_selectedIndexDictionary[indexPath] != nil) cell.accessoryType = UITableViewCellAccessoryCheckmark;

    return cell;


- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath 
    if (_selectedIndexDictionary[indexPath] == nil) 
        [_selectedIndexDictionary setObject:_dataArray[indexPath.row] forKey:indexPath];
        [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryCheckmark];
    else
        [_selectedIndexDictionary removeObjectForKey:indexPath];
        [[tableView cellForRowAtIndexPath:indexPath] setAccessoryType:UITableViewCellAccessoryNone];
    
//    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

@end

【讨论】:

【参考方案8】:

时钟闹钟重复表视图不是多选。把它想象成一串复选框。

当一个单元格被选中时,字体颜色和附件类型会改变并且选择淡出。选择选中的单元格时,返回字体颜色和附件类型,选择淡出。

在您的 didSelectRowAtIndexPath 委托方法中,您将为所选单元格设置文本颜色和附件类型,然后取消选择该单元格。您还将在数据模型中记录新状态。这可以像表示所选状态的位掩码一样简单,但取决于您要显示的数据。

在您的 cellForRowAtIndexPath: dataSource 方法中,根据您的数据模型设置文本颜色和附件类型。

实际的多项选择将是相似的。您必须跟踪选择了哪些单元格,并在创建或显示时设置每个状态的选定单元格。当表格视图报告单元格被选中时,在数据模型中切换选择状态并相应地设置单元格的选中状态。

【讨论】:

【参考方案9】:

您不能关闭 indexPath,因为当您移动时,引用的单元格会发生变化。将 NSLog 放入 cellForRowAtIndexPath 以查看。您可以在 willSelectRowAtIndexPath 或 didSelectRowAtIndexPath 中进行检查/取消检查。不过,这仅涵盖初始检查或取消检查,并且一旦您滚动,也会显示为已检查,因为给定 indexPath 的基础单元格发生了变化。

因此,我找到的解决方案是使用特定于给定单元格的某些选定事物的数组,并进行初始检查。

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 
  UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];

  if (![selectedIndexes containsObject:cell.textLabel.text])
    [selectedIndexes addObject:cell.textLabel.text];
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
   else 
    [selectedIndexes removeObject:cell.textLabel.text];
    cell.accessoryType = UITableViewCellAccessoryNone;
  

  return indexPath;

您还必须在 cellForRowAtIndexPath 中有逻辑,以确保在视图滚动时检查或不检查正确的内容:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

  ...

  cell.accessoryType = UITableViewCellAccessoryNone;
  if ([selectedIndexes containsObject:cell.textLabel.text]) 
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    [cell setSelected:YES animated:YES];
  

  return cell;

【讨论】:

-1 tableView:didDeselectRowAtIndexPath: 中的 indexPath 不依赖于滚动,也不需要保留单独的选定项数组。请参阅@Dmitry 解决方案以获得正确的实施【参考方案10】:

1 - 允许多选和切换选定状态:

tableView.allowsMultipleSelection = true

2 - 完成后收集或获取所有选定索引的数组:

let selected = tableView.indexPathsForSelectedRows

注意:这与屏幕上当前显示的单元格无关。

3 - 根据选定状态更改单元格的外观:在 UITableViewCell 子类中覆盖此方法。如果您没有子类,请创建一个。

// In UITableViewCell subclass
override func setSelected(selected: Bool, animated: Bool) 
    super.setSelected(selected, animated: animated)
    accessoryType = selected ? .Checkmark : .None

【讨论】:

【参考方案11】:

我知道这是一个旧贴,但是为了以后使用,下面的代码将解决确保在滚动过程中选中标记只会出现在选定行上的问题:

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    cell.accessoryType = (cell.isSelected) ? .checkmark : .none

【讨论】:

以上是关于UITableView 多选的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 多选

UITableView更改多选复选标记的颜色

iOS——UITableView单选模式,多选模式,单选多选混合模式

UITableView 多选,预选项

如何为uitableview自定义多选编辑模式

多选模式下的 UITableView 删除选定单元格中所有子视图的背景颜色