English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

iOS Multi-Level List Implementation Code

In project development, hierarchical lists often arise. Simple two-level lists can be implemented using the Header of UITableView, and even simpler three-level lists can be implemented by adjusting the height of the Cell to achieve the effect of a three-level list. However, when encountering multi-level lists, especially dynamic lists with unclear levels, it becomes quite麻烦.

Principle

Hierarchical lists are similar to tree structures, but they are not binary trees, but rather multi-way trees. Each node only needs to have two pointers pointing to the parent node and the child node to form a tree. We regard each level of an object in a multi-level list as a node, and the node has two properties, namely the ID of the parent node and the child node.

Each tree has a virtual root node, its ID is rootID, and all nodes whose parent node ID is rootID are the first level, corresponding to the depth (depth) in the tree structure. In this way, each node object has parentID and childrenID, and childrenID is the ID of the node object.

We can find the first-level node through rootID, and then find the next level according to the childrenID of the first-level node, and so on, to determine the parent-child relationship of all nodes. At the same time, it can also determine the leaf nodes and the first-level nodes, which can also be called

As the root node.

Effect diagram

1.General multi-level list

2.Record the list of historical states of nodes

Thinking

1.Firstly, get all the first-level nodes according to rootID and put them in the data source dataSourceArr of UITableView, and display the initial list

2. Expand: Click the node cell, find the next level nodes according to childrenID, and insert them behind currentNode in dataSourceArr, refresh the display

3. Collapse: Click to expand the node cell, from dataSourceArr's CurrentIndex+1Start, if the level of the node is less than the level of currentNode, remove the node, otherwise stop refreshing the list.

4.Clicking the cell for a leaf node does not respond to expand or collapse operations, and the node information is returned.

dataSourceArr is in such an order that conforms to the hierarchical structure of the tree:

Define a node object

Encountered a problem

1.The problem of local refresh

After each expansion or contraction, refresh the list, initially using

- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation

But it will cause the program to have an overall flickering effect, which is not a good experience. In the end, consider using local refresh insertRowsAtIndexPaths and deleteRowsAtIndexPaths.

But an error will occur during the refresh

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 2 from section 0 which only contains 2 rows before the update'

Speculation is that the inconsistency between numberOfRowsInSection of the current Cell during refresh and numberOfRowsInSection when refreshing insert or delete cells causes the issue. Then try to refresh the current cell and other cells separately, and it refreshes perfectly.

[_reloadArray removeAllObjects];
 [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
 if (currentNode.isExpand) {
  //expand
  [self expandNodesForParentID:currentNode.childrenID insertIndex:indexPath.row];
  [tableView insertRowsAtIndexPaths:_reloadArray withRowAnimation:UITableViewRowAnimationNone];
 }else{
  //fold
  [self foldNodesForLevel:currentNode.level currentIndex:indexPath.row];
   [tableView deleteRowsAtIndexPaths:_reloadArray withRowAnimation:UITableViewRowAnimationNone];
 }

2.How to save the node's historical state

When there are many file-level layers, sometimes it is hoped that after turning off the level and then opening it again, the state of the child layers can still be retained. We can give each node an attribute indicating whether it is expanded, when folded, only modify the expand attribute of currentNode, and when expanded, traverse and insert the child nodes with isexpand=YES.

//expand
- (NSUInteger)expandNodesForParentID:(NSString*parentID insertIndex:(NSUInteger)insertIndex{
 for (int i = 0 ; i<_nodes.count;i++) {
  YKNodeModel *node = _nodes[i];
  if ([node.parentID isEqualToString:parentID]) {
   if (!self.isPreservation) {
    node.expand = NO;
   }
   insertIndex++;
   [_tempNodes insertObject:node atIndex:insertIndex];
   [_reloadArray addObject:[NSIndexPath indexPathForRow:insertIndex inSection:0]];//need to reload nodes
   if (node.isExpand) {
    insertIndex = [self expandNodesForParentID:node.childrenID insertIndex:insertIndex];
   }
  }
 }
 return insertIndex;
}

Demo address:
https://github.com/YangKa/YKMutableLevelTableView.git

That's all for this article. I hope it will be helpful to everyone's learning, and I also hope everyone will support the Yana Nao Tutorial more.

Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously, and this website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report violations, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)

You May Also Like