当用户点击tableView中的一个cell时, 你的App需要响应用户的动作, 这个响应可能是新展示一个TableView, cell中的复选框被选中等等一些动作, 接下就TableView的选中的响应内容进行讲解.
tableView中的cell选中事件
在处理表格视图中的单元格选择时,要记住一些人机界面准则(human-interface guidelines):
- 你不要使用cell的选中来指示状态, 你可以使用cell中的check marks(选择标记, 比如复选框)或accessory view来展示一个状态
- 当用户选择一个cell时, 你应该取消上一次cell的选中(deselect, 通过调用
deselectRowAtIndexPath:animated:
方法), 另外执行一个动作, 比如显示一个detail view. - 如果选中的动作是往navigation controller的栈中push一个新的viewController, 当view controller被pop off(出栈)时, 应该deselect你选中的cell(动画展示).
你可以通过TableView的属性allowsSelectionDuringEditing
控制TableView在编辑模式时是否可选择cell, 另外从iOS 3.0开始, 可以通过allowsSelection
属性来控制非编辑模式下cell是否可选.
响应cell的选中事件
用户点击tableView中的某一行代表用户想选中该行代表的选项或者想进一步探查该行下面隐藏的详细信息. 为了响应用户的动作, 你的APP需要作如下事情:
- 显示数据模型层次结构(data-model hierarchy)中的下一级
- 显示数据模型层次中某一项的详细信息
- 显示一个checkmark, 表示该项被用户选择了
- 如果touch发生在cell中的某个控件上, 那么该touch事件的响应可能由那个控件负责.
tableView通过delegate的tableView:didSelectRowAtIndexPath:
方法来处理点击事件的响应. 代码5-1展示这一方法的实现.
代码清单5-1 响应cell的选中
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];//首先是deselect该行
BATTrailsViewController *trailsController = [[BATTrailsViewController alloc] initWithStyle:UITableViewStylePlain];
trailsController.selectedRegion = [regions objectAtIndex:indexPath.row];
[[self navigationController] pushViewController:trailsController animated:YES];
}
如果你的cell中包含一个disclosure control作为accessory view, 那么当用户点击该control时, delegate会收到tableView:accessoryButtonTappedForRowWithIndexPath:
消息, 而不是tableView:didSelectRowAtIndexPath:
消息.
accessory是可以自定义的, 所以accessoryView是一个switch, slider等control. 代码5-2展示了用一个UISwitch
对象作为accessoryView场景.
代码清单5-2 将switch设置为accessoryView并响应其动作消息
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"CellWithSwitch"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellWithSwitch"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.font = [UIFont systemFontOfSize:14];
}
UISwitch *switchObj = [[UISwitch alloc] initWithFrame:CGRectMake(1.0, 1.0, 20.0, 20.0)];
switchObj.on = YES;
[switchObj addTarget:self action:@selector(toggleSoundEffects:) forControlEvents:(UIControlEventValueChanged | UIControlEventTouchDragInside)];
cell.accessoryView = switchObj;
cell.textLabel.text = @"Sound Effects";
return cell;
}
- (void)toggleSoundEffects:(id)sender {
[self.soundEffectsOn = [(UISwitch *)sender isOn];
[self reset];
}
选择管理对于选择列表也是很重要的。选择列表有两种:
- 排他性列表, 只有一行可选中
- 复选列表, 可以多行选中
代码5-3展示对于一个排他性列表的选中管理. 首先是deselect当前cell, 如果你选中的是同一行则直接返回; 另外将新选中的行的accessory的type设置checkmark, 并将旧选中的cell的accessory的type设置none.
代码清单5-3 排他性列表的选中管理
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSInteger catIndex = [taskCategories indexOfObject:self.currentCategory];
if (catIndex == indexPath.row) {
return;
}
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:catIndex inSection:0];
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
if (newCell.accessoryType == UITableViewCellAccessoryNone) {
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
self.currentCategory = [taskCategories objectAtIndex:indexPath.row];
}
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:oldIndexPath];
if (oldCell.accessoryType == UITableViewCellAccessoryCheckmark) {
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
}
代码5-4展示如果管理多选列表的选中实现.
代码清单5-4 多选列表的实现
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
注意:在
tableView:didSelectRowAtIndexPath:
中总是需要deselect当前选中行
代码控制cell的选择和滚动
有时行的选中源于APP本身, 而不是表视图中的tap. 可能是因为外部引起数据模型导致的, 比如, 用户添加一个新的联系人, 然后返回联系人列表, APP希望将此列表滚动到最近添加的联系人. 为了这种情况, 你可以使用tableView的selectRowAtIndexPath:animated:scrollPosition:
方法或者scrollToNearestSelectedRowAtScrollPosition:animated:
(之前tableView中有选中的行)方法. 你也可能会调用scrollToRowAtIndexPath:atScrollPosition:animated:
使tableView滚动到特定的位置.
代码5-5展示使用selectRowAtIndexPath:animated:scrollPosition
来滚动TableView到特定的行
代码清单5-5 代码控制行的选中
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
NSIndexPath *scrollIndexPath;
if (newIndexPath.row + 20 < [timeZoneNames count]) {
scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row+20 inSection:newIndexPath.section];
} else {
scrollIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row-20 inSection:newIndexPath.section];
}
[theTableView selectRowAtIndexPath:scrollIndexPath animated:YES
scrollPosition:UITableViewScrollPositionMiddle];
}