You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:58:29