子ノードの遅延読み込みのサポートを追加し、Outline サンプル プロジェクトを更新して、同期読み込みと非同期読み込みの両方を示しました。
最初に注意すべきことは、 を持つノードはchildItems == nil
リーフとして扱われるため、View Controller はそれらを展開したり折りたたんだりしようとしないということです。したがって、ノードを遅延ロードする場合は、子アイテムを空の配列に設定する必要があります。
同期ロード用に次のwillChangeNode
デリゲート メソッドが追加されました。
- (TLIndexPathTreeItem *)controller:(TLTreeTableViewController *)controller willChangeNode:(TLIndexPathTreeItem *)treeItem collapsed:(BOOL)collapsed;
子を含む新しいアイテムを返すだけで、既存のアイテムが置き換えられます。新しいアイテムを作成する最も簡単な方法は、次の便利なメソッドを使用することです。
TLIndexPathTreeItem *newTreeItem = [treeItem copyWithChildren:newChildren];
didChangeNode
非同期ロードの場合、デリゲート メソッドでフェッチを開始し、
[self setNewVersionOfItem:newTreeItem];
フェッチの完了ハンドラーのメイン スレッド (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];
}
}];
}
}