【发布时间】:2010-06-14 21:02:20
【问题描述】:
如何将 UITableView 添加到我的基于视图的应用程序中,在该应用程序中,用户将点击多个单元格,它将被选中,就像时钟应用程序的“新闹钟”设置名为“重复”(时钟>闹钟> + >Repeat),我怎样才能得到一个数组中所有选定的单元格?
【问题讨论】:
-
嘘。我没有看到苹果在这方面的例子。
标签: iphone uitableview
如何将 UITableView 添加到我的基于视图的应用程序中,在该应用程序中,用户将点击多个单元格,它将被选中,就像时钟应用程序的“新闹钟”设置名为“重复”(时钟>闹钟> + >Repeat),我怎样才能得到一个数组中所有选定的单元格?
【问题讨论】:
标签: iphone uitableview
多选请在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 }
【讨论】:
在-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;
}
}
除了单元格自己的辅助类型状态之外,您还可以维护一个选定状态数组,或者遍历表视图中的单元格以查询每个单元格的状态以读出选定的行。
【讨论】:
@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;
}
}
【讨论】:
self.tableView.allowsMultipleSelection = YES;
【讨论】:
除了上面的好答案之外,只是一个快速提示:要从时钟应用程序中模仿 Apple 的风格(使行选择颜色在选中/取消选中该行后淡出),将其添加到 didSelectRowAtIndexPath,在条件:
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
来自 Apple 的 TableMultiSelect 指南。
【讨论】:
我在很多开发人员中都看到过这个问题。由于表格视图重用单元格的性质,它会删除或随意进行检查。我为此创建了一个可行的解决方案。从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
【讨论】:
时钟闹钟重复表视图不是多选。把它想象成一串复选框。
当一个单元格被选中时,字体颜色和附件类型会改变并且选择淡出。选择选中的单元格时,返回字体颜色和附件类型,选择淡出。
在您的 didSelectRowAtIndexPath 委托方法中,您将为所选单元格设置文本颜色和附件类型,然后取消选择该单元格。您还将在数据模型中记录新状态。这可以像表示所选状态的位掩码一样简单,但取决于您要显示的数据。
在您的 cellForRowAtIndexPath: dataSource 方法中,根据您的数据模型设置文本颜色和附件类型。
实际的多项选择将是相似的。您必须跟踪选择了哪些单元格,并在创建或显示时设置每个状态的选定单元格。当表格视图报告单元格被选中时,在数据模型中切换选择状态并相应地设置单元格的选中状态。
【讨论】:
您不能关闭 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.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
}
【讨论】:
我知道这是一个旧贴,但是为了以后使用,下面的代码将解决确保在滚动过程中选中标记只会出现在选定行上的问题:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.accessoryType = (cell.isSelected) ? .checkmark : .none
}
【讨论】: