如何从Core Data对象构建多级TableView所需的字典数组
解决多级可展开TableView的Core Data层级构建与展开收起问题
看起来你当前的实现有几个核心问题:层级数据构建逻辑错误、重复加载数据、展开收起的状态判断逻辑不准确。我们一步步来修复这些问题:
1. 给ProductCategoryFilter添加展开状态属性
首先,我们需要给每个分类节点记录是否展开的状态,用这个属性替代不可靠的数组存在性判断,让逻辑更清晰:
// ProductCategoryFilter.h #import <Foundation/Foundation.h> #import "Catal+CoreDataClass.h" @interface ProductCategoryFilter : NSObject @property (nonatomic, strong) Catal* catalCategory; @property (nonatomic, strong) NSMutableArray *arrCatalSubCategory; @property (nonatomic, assign) BOOL isExpanded; // 新增:记录当前节点的展开状态 @end
2. 修正层级数据构建逻辑(核心修复)
你的loadSubCatalData方法错误地将当前父节点包装后加入子数组,导致层级混乱。我们重新实现数据加载方法,正确构建父子层级:
修正loadMainCatalData(加载根节点)
-(void)loadMainCatalData { // 加载id_parent为0的根节点 NSArray *rootCatalList = [Catal fillDataCatal:@"0" :@"0"]; self.arrayOriginal = [NSMutableArray array]; for (Catal *objCatal in rootCatalList) { ProductCategoryFilter *rootFilter = [[ProductCategoryFilter alloc] init]; rootFilter.catalCategory = objCatal; rootFilter.isExpanded = NO; // 默认收起状态 // 递归加载当前根节点的子节点 rootFilter.arrCatalSubCategory = [self loadSubCatalDataForParent:objCatal]; [self.arrayOriginal addObject:rootFilter]; } }
修正loadSubCatalDataForParent(递归加载子节点)
重命名方法明确功能,避免逻辑混淆:
-(NSMutableArray *)loadSubCatalDataForParent:(Catal *)parentCatal{ NSMutableArray *subCatalFilters = [NSMutableArray array]; // 根据父节点的catal_id加载子节点 NSArray *subCatalList = [Catal fillDataCatal:parentCatal.catal_id :@""]; for (Catal *subCatal in subCatalList) { ProductCategoryFilter *subFilter = [[ProductCategoryFilter alloc] init]; subFilter.catalCategory = subCatal; subFilter.isExpanded = NO; // 递归加载当前子节点的子节点 subFilter.arrCatalSubCategory = [self loadSubCatalDataForParent:subCatal]; [subCatalFilters addObject:subFilter]; } return subCatalFilters; }
修正loadDataCatal避免重复加载
- (void)loadDataCatal{ [self loadMainCatalData]; // 直接替换数组,而非追加,防止重复数据 self.arForTable = [self.arrayOriginal mutableCopy]; }
3. 修正ViewDidLoad逻辑,防止重复初始化
- (void)viewDidLoad { [super viewDidLoad]; // 仅当数组为空时加载数据 if (!self.arForTable || self.arForTable.count == 0) { [self loadDataCatal]; } // 移除不可靠的isAlreadyInserted标记,改用isExpanded属性判断状态 }
4. 修正TableView数据源与代理方法
修正cellForRowAtIndexPath,根据状态显示图标
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *CellIdentifier =@"AMG_PP_SubCategoryTableCell"; AMG_PP_SubCategoryTableCell *cell = (AMG_PP_SubCategoryTableCell *) [self.tblProductCategory dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil){ NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil]; cell =(AMG_PP_SubCategoryTableCell *)[nib objectAtIndex:0]; cell.selectionStyle = UITableViewCellSelectionStyleNone; } ProductCategoryFilter *objCat = self.arForTable[indexPath.row]; // 根据子节点数量和展开状态设置图标 if (objCat.arrCatalSubCategory.count > 0) { cell.imgRadio.image = objCat.isExpanded ? [UIImage imageNamed:@"MinusIcon"] : [UIImage imageNamed:@"PlusIcon"]; cell.imgRadio.hidden = NO; } else { // 无子女节点隐藏图标 cell.imgRadio.hidden = YES; } cell.textLabel.text = objCat.catalCategory.title; // 匹配你的Catal对象属性 // 设置缩进体现层级(可选) cell.indentationLevel = [self getLevelForCategory:objCat]; return cell; } // 新增:计算节点层级用于缩进(可根据需求优化) - (NSInteger)getLevelForCategory:(ProductCategoryFilter *)category { // 这里简化处理,若需要准确层级,可在构建ProductCategoryFilter时添加level属性 NSInteger level = 0; // 递归查找父节点层级的逻辑可自行补充 return level; }
重写didSelectRowAtIndexPath,基于isExpanded处理展开收起
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; ProductCategoryFilter *selectedCat = self.arForTable[indexPath.row]; // 无子女节点不处理展开收起 if (selectedCat.arrCatalSubCategory.count == 0) { return; } NSMutableArray *indexPathsToUpdate = [NSMutableArray array]; NSInteger insertPosition = indexPath.row + 1; if (!selectedCat.isExpanded) { // 展开:插入子节点到数组和TableView for (ProductCategoryFilter *subCat in selectedCat.arrCatalSubCategory) { [self.arForTable insertObject:subCat atIndex:insertPosition]; [indexPathsToUpdate addObject:[NSIndexPath indexPathForRow:insertPosition inSection:0]]; insertPosition++; } selectedCat.isExpanded = YES; [tableView insertRowsAtIndexPaths:indexPathsToUpdate withRowAnimation:UITableViewRowAnimationLeft]; } else { // 收起:递归移除所有子节点 [self collapseSubCategories:selectedCat startingAtRow:indexPath.row + 1 indexPaths:indexPathsToUpdate]; selectedCat.isExpanded = NO; [tableView deleteRowsAtIndexPaths:indexPathsToUpdate withRowAnimation:UITableViewRowAnimationRight]; } // 更新当前cell的图标 AMG_PP_SubCategoryTableCell *cell = (AMG_PP_SubCategoryTableCell *)[tableView cellForRowAtIndexPath:indexPath]; cell.imgRadio.image = selectedCat.isExpanded ? [UIImage imageNamed:@"MinusIcon"] : [UIImage imageNamed:@"PlusIcon"]; } // 新增:递归收起所有子节点 - (void)collapseSubCategories:(ProductCategoryFilter *)category startingAtRow:(NSInteger)startRow indexPaths:(NSMutableArray *)indexPaths { NSInteger currentRow = startRow; for (ProductCategoryFilter *subCat in category.arrCatalSubCategory) { [indexPaths addObject:[NSIndexPath indexPathForRow:currentRow inSection:0]]; // 如果子节点是展开状态,递归收起它的子节点 if (subCat.isExpanded) { [self collapseSubCategories:subCat startingAtRow:currentRow + 1 indexPaths:indexPaths]; subCat.isExpanded = NO; } [self.arForTable removeObjectAtIndex:currentRow]; // 移除后元素前移,currentRow无需递增 } }
5. 验证Core Data的fillDataCatal方法
确保该方法正确返回指定父节点的子节点数组,示例实现如下:
// Catal+CoreDataClass.m +(NSArray *)fillDataCatal:(NSString *)parentId :(NSString *)unusedParam { NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Catal"]; NSManagedObjectContext *context = [[CoreDataHelper getInstance] managedObjectContext]; NSError *error; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id_parent == %@", parentId]; [fetchRequest setPredicate:predicate]; // 按order排序保证分类顺序正确 NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES]; [fetchRequest setSortDescriptors:@[sortDescriptor]]; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; if (error) { NSLog(@"Error fetching Catal data: %@", error.localizedDescription); return @[]; } return fetchedObjects; }
这样修改后,应该能解决:
- ViewDidLoad重复添加数据的问题
- 层级数组构建错误的问题
- 展开收起逻辑不准确的问题
如果还有异常,可检查Core Data查询是否正确返回子节点,以及TableView的cell复用逻辑是否正常。
内容的提问来源于stack exchange,提问作者Roshni Sundaresan
相关产品推荐
相关产品推荐

