IOS 多級列表的實現

feng7605 8年前發布 | 8K 次閱讀 iOS開發 移動開發

在項目開發中,層級列表經常遇到,簡單點的二級列表利用UITableView的Header就可以實現,再簡單點的三級列表通過對Cell高度進行調整也可以實現三級列表的效果。但遇到多級列表,尤其是層次不明的動態列表就比較麻煩了。

原理

層級列表和樹形結構比較類似,不過不是二叉樹,而是多叉樹。每個節點只需要擁有指向父節點和子節點的兩個指針,就能形成一顆樹。我們將多級列表中每一級對象看作一個node,node擁有兩個屬性,分別為父節點和子節點的ID。

每棵樹有個一個虛擬的root節點,它的ID為rootID,所有節點中凡是父節點ID為rootID的便是第一級,對應樹結構中的depth(深度)。這樣每一個node對象就都擁有了parentID和childrenID, childrenID為node對象的ID。

我們可以通過rootID查出第一級node,再根據第一級node的childrenID查出下一級,依次類推,確定所有節點的父子關系。同時也可以確定葉子節點和第一級節點,也可稱

為根節點。

效果圖

1.一般多級列表

一般多級列表.gif

2.記錄節點歷史狀態的列表

記錄節點歷史狀態.gif

思路

1.首先根據 rootID 獲取所有第一級節點,并放入UITableView的數據源 dataSourceArr 中,展示初始化列表

2. 展開: 點擊節點cell,根據 childrenID 查找下一級nodes,并插入到 dataSourceArr 中currentNode的后面,刷新展示

3. 收攏: 點擊以打開節點cell,從 dataSourceArr 的CurrentIndex+1開始,如果該節點的level小于currentNode的level,則移除node,否則停止刷新列表。

4.點擊cell為葉子節點則不響應展開或收攏操作,并把節點信息通過返回。

dataSourceArr中是這樣的一種符合樹層級結構的順序:

dataSourceArr中順序.png

定義節點對象

節點對象.png

遇到問題

1.局部刷新的問題

每次展開或收攏以后刷新列表,一開始采用

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

但會導致節目有整體閃爍的效果,體驗不好。最后考慮采用局部刷新 insertRowsAtIndexPaths 和 deleteRowsAtIndexPaths 。

但在刷新中會報錯

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

推測原因是 current Cell在刷新時的numberOfRowsInSection和刷新insert or del的cell時numberOfRowsInSection不一致導致 。然后嘗試current cell和其他cell分別刷新,完美刷新。

[_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.怎么保存節點歷史狀態

當文件級層比較多時,有時希望能關掉層級后再打開時還能保留子層級的打開狀態。我們可以會給每一個node一個是否展開的屬性,當fold時只修改currentNode的expand屬性,expand時對子節點序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 reload nodes

            if (node.isExpand) {
               insertIndex = [self expandNodesForParentID:node.childrenID insertIndex:insertIndex];
            }
        }
    }

    return insertIndex;
}

 

 

來自:http://www.jianshu.com/p/a040ff9e3ae3

 

 本文由用戶 feng7605 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!