实现列表二级展开/收起/选择

Posted demodashi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现列表二级展开/收起/选择相关的知识,希望对你有一定的参考价值。

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

1、先看效果图

技术分享图片

2、实现原理

1)通过对UITableView进行分组来实现展开收起的功能;
2)通过cell的model和组model中的是否选中和是否展开的标记字段来记录并实现展开/收起/选择的功能;

3、代码实现

1)创建cell的model类并添加如下属性:

// 标记是否选中
@property (nonatomic, assign) BOOL isSelect;
// 文字
@property (nonatomic, copy) NSString * title;

其中isSelect为必要属性,主要用来标记该cell是否为选中状态。

2)创建组的model类并添加如下属性:

// 标记是否展开
@property (nonatomic, assign) BOOL isShow;
// 标记是否全选
@property (nonatomic, assign) BOOL isAllSelect;
// 存储该组的数据
@property (nonatomic, strong) NSMutableArray * dataArr;
// 组标题
@property (nonatomic, copy) NSString * title;
@end

其中isShow、isAllSelect、dataArr为必要属性,isShow用来标记该组是否为展开状态,isAllSelect用来标记该组是否为全部选中状态,dataArr用来存储该组的数据。

3)需要在组的model类.m文件中重写init方法,对dataArr属性进行初始化如下:

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        _dataArr = [NSMutableArray new];
    }
    return self;
}

这里对dataArr进行初始化是为了避免在后边使用的时候因为忘记而导致数据存储不上。

4)对cell进行简单的布局和逻辑处理:

#pragma mark - config cell

- (void)configCellWithModel:(RHListModel *)model indexPath:(NSIndexPath *)indexPath isEditing:(BOOL)isEditing {
    
    _lab_title.text = model.title;
    _img_sel.hidden = !isEditing;
    if (isEditing) {
        
        [_lab_title mas_updateConstraints:^(MASConstraintMaker *make) {
            
            make.left.mas_equalTo(60);
        }];
        if (model.isSelect) {
            
//            _img_sel.image = [UIImage imageNamed:@""];
            _img_sel.backgroundColor = Color_Red;
        } else {
            
//            _img_sel.image = [UIImage imageNamed:@""];
            _img_sel.backgroundColor = Color_Blue;
        }
    } else {
        
        [_lab_title mas_updateConstraints:^(MASConstraintMaker *make) {
            
            make.left.mas_equalTo(30);
        }];
    }
}

这里只创建了一个label和一个imageView,其中label用来显示标题,imageView用来显示是否为选中状态。
在该配置cell的方法中通过modelisEditing两个参数来对cell进行相应的布局和更改。其中参数isEditing为该cell是否在编辑状态的标记。

5)对SectionHeaderView进行简单的布局和逻辑处理:

#pragma mark - config section header

- (void)configSectionHeaderWithModel:(RHListSectionModel *)sectionModel isEditing:(BOOL)isEditing tapEventHandler:(void (^)(void))tapHandler selEventHandler:(void (^)(BOOL))selHandler {
    
    _tapEvent = tapHandler;
    _selEvent = selHandler;
    _lab_title.text = sectionModel.title;
    _btn_sel.selected = sectionModel.isAllSelect;
    _img_sel.hidden = !isEditing;
    _btn_sel.hidden = !isEditing;
    if (isEditing) {
        
        [_lab_title mas_updateConstraints:^(MASConstraintMaker *make) {
            
            make.left.mas_equalTo(40);
        }];
        if (sectionModel.isAllSelect) {
            
//            _img_sel.image = [UIImage imageNamed:@""];
            _img_sel.backgroundColor = Color_Red;
        } else {
            
//            _img_sel.image = [UIImage imageNamed:@""];
            _img_sel.backgroundColor = Color_Blue;
        }
    } else {
        
        [_lab_title mas_updateConstraints:^(MASConstraintMaker *make) {
            
            make.left.mas_equalTo(10);
        }];
    }
    if (sectionModel.isShow) {
        
//        _img_arrow.image = [UIImage imageNamed:@""];
        _img_arrow.backgroundColor = Color_Yellow;
    } else {
        
//        _img_arrow.image = [UIImage imageNamed:@""];
        _img_arrow.backgroundColor = Color_Purple;
    }
}
@end

这里通过对headerView添加手势的方式对其添加点击事件,由于需要在外部获取到该点击事件,所以这里使用了block回调的方式来回调点击该组和点击选择按钮的两个事件。其中tapHandler为点击该组时的回调,selHandler为点击选择按钮时的回调。在这里分别定义了两个block属性来接收并在点击事件中进行回调赋值。
这里对标题和选择按钮图片的处理与在cell中的类似,这里不再多说。

6)在VC中实现布局及逻辑处理:

#pragma mark - load data

- (void)loadData {
    
    for (int i = 0; i < 5; i++) {
        
        RHListSectionModel * sectionModel = [[RHListSectionModel alloc] init];
        sectionModel.title = [NSString stringWithFormat:@"第%d组", i + 1];
        for (int j = 0; j < arc4random()%5 + 4; j++) {
            
            RHListModel * model = [[RHListModel alloc] init];
            model.title = [NSString stringWithFormat:@"第%d组,第%d个", i + 1, j + 1];
            [sectionModel.dataArr addObject:model];
        }
        [_dataArr addObject:sectionModel];
    }
    [_tableView reloadData];
}

首先这里简单模拟加载数据。

#pragma mark - btn event

- (void)clickEdit:(UIButton *)sender {
    
    sender.selected = !sender.selected;
    if (sender.selected) {
        
        for (RHListSectionModel * sectionModel in self.dataArr) {
            
            sectionModel.isAllSelect = NO;
            for (RHListModel * model in sectionModel.dataArr) {
                
                model.isSelect = NO;
            }
        }
    }
    [_tableView reloadData];
}

这是在导航上边添加了一个编辑按钮的点击事件,点击按钮切换按钮的选中状态。通过此来切换列表是否为编辑状态。
这里是在按钮选中前对所有数据的选中状态进行了初始化,即全部设置为未选中。如果有需求需要记住编辑时的状态,这里的if判断可以去掉即可。

#pragma mark - tableView dataSource and delegate

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
    return self.dataArr.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    RHListSectionModel * sectionModel = self.dataArr[section];
    if (!sectionModel.isShow) {
        
        return 0;
    }
    return sectionModel.dataArr.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    RHListCell * cell = [tableView dequeueReusableCellWithIdentifier:@"RHListCell" forIndexPath:indexPath];
    RHListSectionModel * sectionModel = self.dataArr[indexPath.section];
    RHListModel * model = sectionModel.dataArr[indexPath.row];
    [cell configCellWithModel:model indexPath:indexPath isEditing:self.btn_edit.selected];
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    
    RHListSectionHeaderView * headerView = [[RHListSectionHeaderView alloc] init];
    RHListSectionModel * sectionModel = self.dataArr[section];
    [headerView configSectionHeaderWithModel:sectionModel isEditing:self.btn_edit.selected tapEventHandler:^{
        
        sectionModel.isShow = !sectionModel.isShow;
        [tableView reloadData];
    } selEventHandler:^(BOOL isAllSelect) {
        
        sectionModel.isAllSelect = isAllSelect;
        if (isAllSelect) {
            
            for (RHListModel * model in sectionModel.dataArr) {
                
                model.isSelect = YES;
            }
        } else {
            
            for (RHListModel * model in sectionModel.dataArr) {
                
                model.isSelect = NO;
            }
        }
        [tableView reloadData];
    }];
    return headerView;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    if (!self.btn_edit.selected) {
        
        return;
    }
    RHListSectionModel * sectionModel = self.dataArr[indexPath.section];
    RHListModel * model = sectionModel.dataArr[indexPath.row];
    model.isSelect = !model.isSelect;
    if (model.isSelect) {
        
        int count = 0;
        for (RHListModel * mod in sectionModel.dataArr) {
            
            if (mod.isSelect) {
                
                count++;
            }
        }
        if (count == sectionModel.dataArr.count) {
            
            sectionModel.isAllSelect = YES;
        }
    } else {
        
        sectionModel.isAllSelect = NO;
    }
    [tableView reloadData];
}

这里是tableView的主要代理实现方法主要说一下返回SectionHeader及点击cell的两个代理。
其中返回SectionHeader的代理里边使用定制的SectionHeader并调用外漏的配置方法,在block里边进行展开/收起/选中/取消选中该组的处理,最后对tableView进行刷新。
cell的点击事件里边主要是对在点击cell时的处理,如果在编辑状态下,点击cell时需要统计该组中选中cell的个数,来实时改变改组的组的选中状态。

4、项目结构

主要采用MVC的方式来实现
技术分享图片

OK!至此所有功能已经实现,我们在使用的时候可以根据自己的需求对model和cell进行相应的修改。实现列表二级展开/收起/选择

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

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










以上是关于实现列表二级展开/收起/选择的主要内容,如果未能解决你的问题,请参考以下文章

wpf 列表菜单 收起与展开,通过Grid DoubleAnimation或者Expander实现

实现列表展开收起效果

在选择画面中收起/展开字段

element中树形数据与懒加载实现全部展开和全部收起

iOS-文本内容展开/收起实现方案

通过css控制div内容展开更多/收起效果