Let's start from basic idea. I'll focus on expand/collapse functionality, ignoring cell types (switches, textfields, whatever). Table view hierarchy is limited with section level and row level. The requirement is to implement any number of nested levels. But we could indent cells using indentationLevel
property. Table view would consist of one section with expanded (filtered) cells with level information provided. Therefore, goal is to transform deep plist hierarchy to flat one which is easier to manipulate when populating table view.
The structure of a plist node.
Node contains following properties:
- name (cell title)
- level (indentation level, hardcoded in plist for simplicity)
- collapsed (wether node is collapsed, changed in response of user interaction)
- children (child nodes in hierarchy)

Root element must be a dictionary, if you use dictionaryWithContentsOfFile:
for plist loading. To keep the order of top level nodes unchanged we use array as a root container.
Data structure
Plist hierarchy is represented by NSMutableDictionary
(mutability is required because we will change collapsed
property). Table view flat hierarchy is represented by NSMutableArray
(stay tuned).
@interface ViewController ()
{
NSMutableDictionary *_model;
NSMutableArray *_items;
}
@end
Transform plist hierarchy to flat one
Possible solution is to iterate over all nodes, choose expanded only and add them to a flat array. It could be achieved recursively:
- (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;
}
Populate table view
We need to set indentation level and respond to user interaction (collapse/expand) cells.
- (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
retransforms plist hierarchy with new changes. If you want to animate changes you need extra logic for inserting/deleting expanded/collapsed cells.