效果图
Demo
背景
最近实现了一个部门选择器,包含多级部门的显示,全选以及反选功能,运用到了树状结构的概念
准备工作
- NRDesignateGroupModel 用来表示部门的Model,包含父节点和子groups数据,负责父checked的指向和子groups的checked的遍历
- NRBreadcrumView 面包屑View,负责层级的展示,可进行查看不同层级数据的操作
- NRDesignateDeptCell 部门的Cell,包含部门名称、选择和取消选择的按钮功能
- NRDesignateChoiceTableHeaderView 全选按钮的View
- 一个tableView用来展示当前节点的部门列表
Controller解析
面包屑要注意与下方tableView的联动,tableView点击的时候触发面包屑的标题添加,面包屑点击的时候触发节点的变更,从而变更tableView的数据
面包屑的点击
///修改面包屑的节点数组长度
-(void)breadcrumViewDidSelectedGroupModel:(NRDesignateGroupModel *)groupModel{
///当前节点点击不做处理
if(![self.currentGroupNodeModel.departmentId isEqualToString:groupModel.departmentId]){
NSInteger index = [self.breadcrumArray indexOfObject:groupModel];
NSArray *array = [self.breadcrumArray subarrayWithRange:NSMakeRange(0, index+1)];
self.breadcrumArray = [NSMutableArray arrayWithArray:array];
self.breadcrumView.breadcrumArray = array;
[self updateCurrentGroupModel:groupModel];
}
}
///触发节点改变,改变tableView的数据
- (void)updateCurrentGroupModel:(NRDesignateGroupModel *)groupModel{
self.currentGroupNodeModel = groupModel;
self.allSelectView.checked = groupModel.checked;
[self getDepartmentGroupData:groupModel];
}
复制代码
tableView的点击
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NRDesignateGroupModel *model = [self.currentGroupNodeModel.groups objectAtIndex:indexPath.row];
[self updateBreadcrumWithGroupData:model];
}
///触发面包屑的改变。从而触发tableView的改变,与上面面包屑的流程就一致了
- (void)updateBreadcrumWithGroupData:(NRDesignateGroupModel *)groupModel{
[self.breadcrumArray addObject:groupModel];
self.breadcrumView.breadcrumArray = self.breadcrumArray;
[self updateCurrentGroupModel:groupModel];
}
复制代码
单选
///修改model的checked,updateChecked在model内部实现,遍历子groups的checked和检查父节点的checked(当父节点的groups全选中的时候要checked=YES),从而显示allSelectView的checked
- (void)selectedOnceBtn:(NSIndexPath *)indexPath{
NRDesignateGroupModel *groupModel = [self.currentGroupNodeModel.groups objectAtIndex:indexPath.row];
[groupModel updateChecked:!groupModel.checked];
self.allSelectView.checked = self.currentGroupNodeModel.checked;
}
复制代码
全选
///当前节点调用updateChecked,作用于上面单选一致,遍历子和父
- (void)selectedAllBtn{
[self.currentGroupNodeModel updateChecked:!self.currentGroupNodeModel.checked];
[self.tableView reloadData];
}
复制代码
提交遍历
///allFlag是只表示第一级部门的全选和非全选
///遍历到当前节点checked为YES时,停止遍历,因为下面的所有子部门一定全选
///当上面遍历完之后,我们遍历之前选中但是后来因为代码的操作影响了checked的,也要传给服务器,服务器要做减法处理,不做的可以不考虑。
///然后一级一级遍历下去
- (void)traversalGroupNode:(NRDesignateGroupModel *)groupNode{
///第一个循环找出被选中的
if(groupNode.checked){
if(groupNode.departmentId){
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"departmentId"] = groupNode.departmentId;
dict[@"checked"] = @"1";
dict[@"name"] = groupNode.name;
[self.groupChangedArray addObject:dict];
}else{
self.allFlag = 1;
}
return;
}
///第一个循环找出之前被选中然后被其他代码取消选中的
if(groupNode.codeChanged){
if(groupNode.departmentId){
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"departmentId"] = groupNode.departmentId;
dict[@"checked"] = @"0";
dict[@"name"] = groupNode.name;
[self.groupChangedArray addObject:dict];
}else{
self.allFlag = 2;
}
}
for(NRDesignateGroupModel *model in groupNode.groups){
[self dfsGroupNode:model];
}
}
复制代码
Model的解析
updateChecked
///codeChanged 是在Controller里用来获取到部门数据之后,根据父节点的checked来显示当前节点的checked
- (void)updateChecked:(BOOL)checked{
self.checked = checked;
self.codeChanged = YES;
[self setAllChildChecked:checked];
if(self.parentGroupModel){
[self.parentGroupModel isAllChecked];
}
}
///遍历子groups的checked
- (void)setAllChildChecked:(BOOL)checked{
for(NRDesignateGroupModel *groupModel in self.groups){
if(groupModel.checked != checked){
groupModel.checked = checked;
groupModel.codeChanged = YES;
[groupModel setAllChildChecked:checked];
}
}
}
///遍历父节点的checked
- (void)isAllChecked{
BOOL flag = YES;
for(NRDesignateGroupModel *groupModel in self.groups){
if(groupModel.checked == NO){
flag = NO;
}
}
if(self.checked != flag){
self.checked = flag;
self.codeChanged = YES;
if(self.parentGroupModel){
[self.parentGroupModel isAllChecked];
}
}
}
复制代码
总结
用到了递归的知识,总的结构可以看成一个树状结构,每个节点下都保留了父节点和子groups这是关键,这样我们不需要知道当前到第几级,只要拿到当前节点,就可以显示和操作数据了。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END