让我们从基本概念开始。我将专注于展开/折叠功能,忽略单元格类型(开关、文本字段等)。表视图层次结构受节级别和行级别的限制。要求是实现任意数量的嵌套级别。但是我们可以使用indentationLevel 属性来缩进单元格。表格视图将由一个部分组成,其中包含提供级别信息的扩展(过滤)单元格。因此,目标是将深层 plist 层次结构转换为扁平层次结构,这在填充表格视图时更易于操作。
plist 节点的结构。
节点包含以下属性:
- 姓名(单元格标题)
- level(缩进级别,为简单起见在 plist 中硬编码)
- 折叠(节点是否折叠,响应用户交互而更改)
- children(层次结构中的子节点)
根元素必须是字典,如果您使用dictionaryWithContentsOfFile: 进行plist 加载。为了保持顶级节点的顺序不变,我们使用数组作为根容器。
数据结构
Plist 层次结构由NSMutableDictionary 表示(需要可变性,因为我们将更改collapsed 属性)。表视图平面层次结构由NSMutableArray 表示(敬请期待)。
@interface ViewController ()
{
NSMutableDictionary *_model;
NSMutableArray *_items;
}
@end
将 plist 层次结构转换为平面层次结构
可能的解决方案是遍历所有节点,选择仅扩展并将它们添加到平面数组中。可以递归实现:
- (void)reloadExpandedItems
{
for (NSDictionary *item in _model[@"items"]) {
[_items addObject:item];
[self reloadExpandedItemsForItem:item];
}
}
- (void)reloadExpandedItemsForItem:(NSDictionary*)item
{
if ([item[@"collapsed"] boolValue]) {
return;
}
for (NSDictionary *child in item[@"children"]) {
[_items addObject:child];
[self reloadExpandedItemsForItem:child];
}
return;
}
填充表格视图
我们需要设置缩进级别并响应用户交互(折叠/展开)单元格。
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [_items[indexPath.row][@"level"] integerValue];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *item = _items[indexPath.row];
item[@"collapsed"] = @(![item[@"collapsed"] boolValue]); // inverse bool value
[self reloadItems];
}
- (void)reloadItems
{
[_items removeAllObjects];
[self reloadExpandedItems];
[self.tableView reloadData];
}
reloadItems 用新的变化重新转换 plist 层次结构。如果您想为更改设置动画,则需要额外的逻辑来插入/删除展开/折叠的单元格。