我正在使用新的TLIndexPathTools library来创建动态iOS表格视图。具体来说,使用TLIndexPathTreeItem类构建分层下拉表结构。
从“Outline”示例中,似乎必须从叶节点到父节点静态生成整个层次结构:
//setup item heirarchy for data model
TLIndexPathTreeItem *item111 = [self itemWithId:ITEM_1_1_1 level:2 children:nil];
TLIndexPathTreeItem *item112 = [self itemWithId:ITEM_1_1_2 level:2 children:nil];
TLIndexPathTreeItem *item11 = [self itemWithId:ITEM_1_1 level:1 children:@[item111, item112]];
TLIndexPathTreeItem *item121 = [self itemWithId:ITEM_1_2_1 level:2 children:nil];
TLIndexPathTreeItem *item12 = [self itemWithId:ITEM_1_2 level:1 children:@[item121]];
TLIndexPathTreeItem *item1 = [self itemWithId:ITEM_1 level:0 children:@[item11, item12]];
TLIndexPathTreeItem *item211 = [self itemWithId:ITEM_2_1_1 level:2 children:nil];
TLIndexPathTreeItem *item221 = [self itemWithId:ITEM_2_2_1 level:3 children:nil];
TLIndexPathTreeItem *item212 = [self itemWithId:ITEM_2_1_2 level:2 children:@[item221]];
TLIndexPathTreeItem *item21 = [self itemWithId:ITEM_2_1 level:1 children:@[item211, item212]];
TLIndexPathTreeItem *item2 = [self itemWithId:ITEM_2 level:0 children:@[item21]];
但是,我有大约1,000行数据,在每个叶子之前有2-4个级别。有没有办法在点击时动态填充每个下拉列表?否则我需要一个巨大的递归数据库调用和一些其他hackery来将整个树结构加载到内存中,然后以某种方式将其从叶子静态设置为父级,如示例中所示。
答案 0 :(得分:2)
我添加了对延迟加载子节点的支持,并更新了Outline sample project以演示同步和异步加载。
首先要注意的是,具有childItems == nil
的节点被视为叶子,因此视图控制器不会尝试展开或折叠它们。因此,如果要延迟加载节点,则需要将子项设置为空数组。
为同步加载添加了以下willChangeNode
委托方法:
- (TLIndexPathTreeItem *)controller:(TLTreeTableViewController *)controller willChangeNode:(TLIndexPathTreeItem *)treeItem collapsed:(BOOL)collapsed;
只需返回包含子项的新项目,它将替换现有项目。创建新项目的最简单方法是使用以下便捷方法:
TLIndexPathTreeItem *newTreeItem = [treeItem copyWithChildren:newChildren];
对于异步加载,在didChangeNode
委托方法中启动提取并调用
[self setNewVersionOfItem:newTreeItem];
在fetch的完成处理程序中的主线程上(其中self
是TLTreeTableViewController
):
编辑要详细说明异步延迟加载,例如,如果您从服务器获取数据,它将如下所示:
- (void)controller:(TLTreeTableViewController *)controller didChangeNode:(TLIndexPathTreeItem *)treeItem collapsed:(BOOL)collapsed
{
//the logic here checks that the node is being opened. Note that this will cause the
//node to refresh every time it's expanded. If we only want to load children once,
//we need to keep track of which nodes have been loaded (or maybe just check
//that there are no child items yet: [treeItem.childItmes count] == 0).
if (collapsed == NO) {
//the next steps is to initiate a data fetch. So the hypothetical method
//`fetchChildDataFromServerForItem` would execute the appropriate fetch for
//the given item (to identify the item, one would typically look at the
//`treeItem.identifier` property or maybe some information that has been placed
//in `treeItem.data`.
[self fetchChildDataFromServerForItem:treeItem completion:^(BOOL success, NSArray *result){
//The fetch method would presumably have a completion block, where the result
//data would be inserted into the tree.
if (success && [result count] > 0) {
//we build up an array of child items by looping over the result set. for arguments
//sake, assume the result items are dictionaries (e.g. JSON data). They could just
//as well be Core Data objects, strings, etc.
NSMutableArray *childItems = [NSMutableArray arrayWithCapacity:result.count];
for (NSDictionary *data in result) {
//TLIndexPathTools relies on items being identifiable, so we need to determine
//an appropriate identifier for our data. It could be the data object itself if it
//has a suitable `isEqual` method, such as with `NSString`. For a Core Data object,
//we would use the `objectID`. But for our dictionary items, we'll assume the
//dictionary contains a `recordId`.
NSString *identifier = [data valueForKey:@"recordId"];
//now we wrap our data in a tree item and add it to the array
TLIndexPathTreeItem *childItem = [[TLIndexPathTreeItem alloc] initWithIdentifier:identifier sectionName:nil cellIdentifier:@"CellIdForThisLevel" data:data andChildItems:nil];
[childItems addObject:childItem];
}
//now we generate a new new node by copying the existing node, but providing a new set of children
TLIndexPathTreeItem *newTreeItem = [treeItem copyWithChildren:childItems];
//and finally ask the view controller to set our new tree item, which will cause the new
//child items to appear in the table.
[self setNewVersionOfItem:newTreeItem];
} else {
//perhaps if the fetch fails to return any items, we set the child items to nil
//to indicate to the controller that this is a leaf node.
TLIndexPathTreeItem *newTreeItem = [treeItem copyWithChildren:nil];
[self setNewVersionOfItem:newTreeItem];
}
}];
}
}