简体   繁体   English

UITableView 多选

[英]UITableView Multiple Selection

如何将 UITableView 添加到我的基于视图的应用程序中,用户将在其中点击多个单元格,并且它将被选中,就像时钟应用程序的“新闹钟”设置名为“重复”(时钟>闹钟> + >重复),以及如何获取数组中的所有选定单元格?

For multiple selection, add the line below in viewDidLoad()对于多选,在viewDidLoad()添加以下行

tableView.allowsMultipleSelection = true

Configure each cell after dequeuing (or initializing) it in tableView(_:cellForRowAt:)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

Update each cell when it's selected/deselected选择/取消选择时更新每个单元格

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
}

When you're done, get an array of all the selected rows完成后,获取所有选定行的数组

let selectedRows = tableView.indexPathsForSelectedRows

and get the selected data, where dataArray maps to the rows of a table view with only 1 section并获取选定的数据,其中dataArray映射到只有 1 个部分的表视图的行

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

In your implementation of -tableView:didSelectRowAtIndexPath: you would set the table view cell's accessoryType property depending on its current value (so it would toggle on and off with multiple taps).在您的-tableView:didSelectRowAtIndexPath:实现中,您将根据当前值设置表格视图单元格的accessoryType属性(因此它可以通过多次点击打开和关闭)。 For example:例如:

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

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

You could either maintain an array of selected states in addition to the cells' own accessory type state, or iterate over the cells in the table view querying for each one's state in order to read out the selected rows.除了单元格自己的附件类型状态之外,您还可以维护一组选定状态,或者遍历表视图中的单元格,查询每个单元格的状态以读出选定的行。

@BrendanBreg implementation didn't worked for me. @BrendanBreg实现对我不起作用。 @RaphaelOliveira provided good solution , but when you scrolls your table down - wrong rows become selected (because UITableView caches it's cells). @RaphaelOliveira 提供了很好的解决方案,但是当您向下滚动表格时 - 选择了错误的行(因为UITableView缓存了它的单元格)。 So, I've slightly modified Raphael's solution:所以,我稍微修改了 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;
    }
}
self.tableView.allowsMultipleSelection = YES;

Just a quick tip in addition to the great answer above: to mimic Apple's style from the clock app (making the row select color fade back out after checking / unchecking the row), add this to the didSelectRowAtIndexPath , after the conditionals:除了上面的好答案之外,还有一个快速提示:从时钟应用程序中模仿 Apple 的风格(在选中/取消选中行后使行选择颜色淡出),将其添加到didSelectRowAtIndexPath ,在条件之后:

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

From Apple's TableMultiSelect guide.来自 Apple 的TableMultiSelect指南。

I have seen this problem with so many developers.我在很多开发人员身上都看到过这个问题。 Due to table view's nature of re-using cell it removes or haphazard the checks.由于表视图的重用单元格的性质,它删除或随意检查。 I have created a working solution for that.我为此创建了一个有效的解决方案。 Clone/download code from DevelopmentSupportWorkspace and Execute UITableViewTest project from there.DevelopmentSupportWorkspace克隆/下载代码并从那里执行UITableViewTest项目。

Here is code summery for that:这是代码总结:

@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

You can't key off indexPath because the cells that refers to changes as you scoll.您无法关闭 indexPath,因为引用的单元格会随着您的滚动而发生变化。 Put an NSLog in cellForRowAtIndexPath to see that.将 NSLog 放在 cellForRowAtIndexPath 中以查看。 You can do the check/uncheck in willSelectRowAtIndexPath or didSelectRowAtIndexPath.您可以在 willSelectRowAtIndexPath 或 didSelectRowAtIndexPath 中进行选中/取消选中。 That covers only the initial check or uncheck though, and will also have things appear as checked once you've scrolled because the underlying cell for a given indexPath changes.不过,这仅涵盖初始检查或取消检查,并且一旦您滚动,由于给定 indexPath 的基础单元格发生更改,也会使内容显示为已检查。

So the solution I found is to have an array of selected things with something that is specific to that given cell, and do the initial check.因此,我找到的解决方案是使用特定于该给定单元格的内容选择一组内容,并进行初始检查。

- (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;
}

You also have to have logic in cellForRowAtIndexPath to make sure the right stuff is checked or not as the view scrolls:您还必须在 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;
}

The clock alarms repeat table view is not multiple selection.时钟闹钟重复表视图不是多选。 Think of it as a lit of checkboxes.把它想象成一堆复选框。

When a cell is selected, the font color and accessory type are changed and the selection fades out.当一个单元格被选中时,字体颜色和附件类型会改变,并且选择会淡出。 When a checked cell is selected, the font color and accessory type are changed back and the selection fades out.选择选中的单元格时,字体颜色和附件类型会更改,并将选择逐渐消失。

In your didSelectRowAtIndexPath delegate method, you would set the text color and accessory type for the selected cell, then deselect the cell.在您的 didSelectRowAtIndexPath 委托方法中,您将为所选单元格设置文本颜色和附件类型,然后取消选择该单元格。 You would also record the new state in your data model.您还将在数据模型中记录新状态。 That could be as simple as a bit mask representing the selected state, but depends on what data you are displaying.这可能就像表示所选状态的位掩码一样简单,但取决于您正在显示的数据。

In your cellForRowAtIndexPath: dataSource method, set the text color and accessory type based on your data model.在您的 cellForRowAtIndexPath: dataSource 方法中,根据您的数据模型设置文本颜色和附件类型。

Actual multiple selection would be similar.实际的多项选择将是相似的。 You have to keep track of which cells are selected, and set the selected cell of each state as it is created or shown.您必须跟踪选择了哪些单元格,并在创建或显示时设置每个状态的选定单元格。 When the table view reports that a cell is selected, toggle the selection state in your data model and set the selected state of the cell accordingly.当表格视图报告单元格被选中时,切换数据模型中的选择状态并相应地设置单元格的选中状态。

1 - Allow multiple selection and toggle of selected state: 1 - 允许多选和选中状态切换:

tableView.allowsMultipleSelection = true

2 - Collect or get an array of all selected indices when you are done: 2 - 完成后收集或获取所有选定索引的数组:

let selected = tableView.indexPathsForSelectedRows

Note: This is independent of which cells are currently showing on screen.注意:这与当前在屏幕上显示的单元格无关。

3 - Change the appearance of the cell depending on selected state: Override this method in your UITableViewCell subclass. 3 - 根据选定状态更改单元格的外观:在 UITableViewCell 子类中覆盖此方法。 If you don't have a subclass, make one.如果您没有子类,请创建一个。

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

I know that this is an old posting, but for future use, the following code will solve the problem of ensuring that a checkmark will only appear on selected rows during scrolling:我知道这是一个旧帖子,但为了将来使用,以下代码将解决确保在滚动期间仅在选定行上出现复选标记的问题:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM