zoukankan      html  css  js  c++  java
  • iOS TableView多级列表

    代码地址如下:
    http://www.demodashi.com/demo/15006.html

    效果预览

    TableView多级列表的实现效果预览图

    一、需求

    TableView多级列表:分级展开或合并,逐级获取并展示其子级数据,可以设置最大的层级数,支持多选、单选、取消选择。

    二、思路

    由需求和示意图可知,这些数据元素之间存在着一对多关系,很符合 数据结构与算法 -- 树形结构 的特征。那么,我们就用树形结构中的结点(Node)来作为存储和关联数据的模型(NodeModel)。

    
    //每个结点信息,采用的是树状结构模型
     关于树状结构不了解的可以看看我的这篇文章 https://www.jianshu.com/p/c545c93f2585
    
    @interface SLNodeModel : NSObject
    
    @property (nonatomic, strong) NSString *parentID; // 父结点ID 即当前结点所属的的父结点ID
    
    @property (nonatomic, strong) NSString *childrenID; //子结点ID 即当前结点的ID
    
    @property (nonatomic, strong) NSString *name; //结点名字
    
    @property (nonatomic, assign) int level; // 结点层级 从1开始
    
    @property (nonatomic, assign) BOOL leaf;  // 树叶(Leaf) If YES:此结点下边没有结点咯;
    
    @property (nonatomic, assign) BOOL root;  // 树根((Root) If YES: parentID = nil
    
    @property (nonatomic, assign) BOOL expand; // 是否展开
    
    @property (nonatomic, assign) BOOL selected; // 是否选中
    
    @end
    
    

    三、实现

    层级状态: 根据传入的层级数来调整层级UI状态。

    展开或合并: 通过插入或删除cell的方式来实现。(示例中的数据都是假数据,随机生成的。)

    插入和删除的位置以及范围可通过点击的结点的位置、层级、子结点ID(当前结点ID)与子结点的层级或父节点相比较来确定。可以的话,做一下缓存处理,优化不分大小,从点滴做起

    
    /**
     获取并展开父结点的子结点数组 数量随机产生
     @param level 父结点的层级
     @param indexPath 父结点所在的位置
     */
    - (void)expandChildrenNodesLevel:(int)level atIndexPath:(NSIndexPath *)indexPath {
        NSMutableArray * insertNodeRows = [NSMutableArray array];
        int insertLocation = (int)indexPath.row + 1;
        for (int i = 0; i < arc4random()%9; i++) {
            SLNodeModel * node = [[SLNodeModel alloc] init];
            node.parentID = @"";
            node.childrenID = @"";
            node.level = level + 1;
            node.name = [NSString stringWithFormat:@"第%d级结点",node.level];
            node.leaf = (node.level < MaxLevel) ? NO : YES;
            node.root = NO;
            node.expand = NO;
            node.selected = NO;
            [self.dataSource insertObject:node atIndex:insertLocation + i];
            [insertNodeRows addObject:[NSIndexPath indexPathForRow:insertLocation + i inSection:0]];
        }
        
        //插入cell
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithArray:insertNodeRows] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];
        
        //更新新插入的元素之后的所有cell的cellIndexPath
        NSMutableArray * reloadRows = [NSMutableArray array];
        int reloadLocation = insertLocation + (int)insertNodeRows.count;
        for (int i = reloadLocation; i < self.dataSource.count; i++) {
            [reloadRows addObject:[NSIndexPath indexPathForRow:i inSection:0]];
        }
        [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationNone];
    }
    
    /**
     获取并隐藏父结点的子结点数组
     @param level 父结点的层级
     @param indexPath 父结点所在的位置
     */
    - (void)hiddenChildrenNodesLevel:(int)level atIndexPath:(NSIndexPath *)indexPath {
        NSMutableArray * deleteNodeRows = [NSMutableArray array];
        int length = 0;
        int deleteLocation = (int)indexPath.row + 1;
        for (int i = deleteLocation; i < self.dataSource.count; i++) {
            SLNodeModel * node = self.dataSource[i];
            if (node.level > level) {
                [deleteNodeRows addObject:[NSIndexPath indexPathForRow:i inSection:0]];
                length++;
            }else{
                break;
            }
        }
        [self.dataSource removeObjectsInRange:NSMakeRange(deleteLocation, length)];
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:deleteNodeRows withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];
        
        //更新删除的元素之后的所有cell的cellIndexPath
        NSMutableArray * reloadRows = [NSMutableArray array];
        int reloadLocation = deleteLocation;
        for (int i = reloadLocation; i < self.dataSource.count; i++) {
            [reloadRows addObject:[NSIndexPath indexPathForRow:i inSection:0]];
        }
        [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationNone];
    }
    
    

    选中: 会更新当前结点下所有子结点的选中状态。

    选中的位置以及范围可通过点击的结点的位置、层级、子结点ID(当前结点ID)与子结点的层级或父节点相比较来确定。可以的话,做一下缓存处理,优化不分大小,从点滴做起

    
    /**
     更新当前结点下所有子节点的选中状态
     @param level 选中的结点层级
     @param selected 是否选中
     @param indexPath 选中的结点位置
     */
    - (void)selectedChildrenNodes:(int)level selected:(BOOL)selected atIndexPath:(NSIndexPath *)indexPath {
        NSMutableArray * selectedNodeRows = [NSMutableArray array];
        int deleteLocation = (int)indexPath.row + 1;
        for (int i = deleteLocation; i < self.dataSource.count; i++) {
            SLNodeModel * node = self.dataSource[i];
            if (node.level > level) {
                node.selected = selected;
                [selectedNodeRows addObject:[NSIndexPath indexPathForRow:i inSection:0]];
            }else{
                break;
            }
        }
        [self.tableView reloadRowsAtIndexPaths:selectedNodeRows withRowAnimation:UITableViewRowAnimationNone];
    }
    
    

    四、项目结构

    iOS TableView多级列表

    代码地址如下:
    http://www.demodashi.com/demo/15006.html

    注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    C++ 注意事项
    Google protocol buffer的配置和使用(Linux&&Windows)
    网络编程的一些错误总结
    C++单例模式设计与实现
    TCP粘包处理
    C++ 局部静态变量,全局变量,全局静态变量,局部变量的区别和联系
    C++模板类注意事项
    Django框架(二十二)—— Django rest_framework-频率组件
    Django框架(二十一)—— Django rest_framework-权限组件
    Django框架(二十)—— Django rest_framework-认证组件
  • 原文地址:https://www.cnblogs.com/demodashi/p/10486806.html
Copyright © 2011-2022 走看看