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的方法中通过model
和isEditing
两个参数来对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进行相应的修改。实现列表二级展开/收起/选择
注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权