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

SwiftUI中如何按Category分组Core Data的Item列表?

原代码的问题在于:

  • 最初的实现遍历所有Item,每个Item生成独立的Section,导致同分类的Item分散在多个Section中
  • 修改后的实现依然遍历每个Item生成Section,再在Section内显示同分类的所有Item,同一个分类会被重复生成多次Section

以下是两种可行的解决思路:


方案一:视图层直接分组处理

无需修改服务层,在视图内完成Item的分类分组,再生成对应Section。

  1. 让Core Data的Category实体遵守Identifiable协议(利用Core Data对象自带的唯一objectID):
extension Category: Identifiable {
    public var id: ObjectIdentifier { objectID }
}
  1. 修改ItemsView代码,添加分组计算属性并重构列表:
struct ItemsView: View {
    let selectedList: List

    @EnvironmentObject private var itemSM: ItemServiceModel
    
    // 按Category对Item分组,自动过滤无分类的Item
    private var groupedItems: [Category: [Item]] {
        Dictionary(
            grouping: itemSM.items.filter { $0.category != nil },
            by: { $0.category! }
        )
    }
    
    var body: some View {
        List{
            // 按分类名称排序后遍历每个分类
            ForEach(Array(groupedItems.keys).sorted { $0.name ?? "" < $1.name ?? "" }) { category in
                Section(header: Text(category.name ?? "未分类")) {
                    // 对当前分类下的Item按名称排序后显示
                    ForEach(groupedItems[category]!.sorted { $0.name ?? "" < $1.name ?? "" }) { item in
                        Text(item.name ?? "")
                    }
                }
            }
            
            // 可选:单独显示无分类的Item
            let uncategorizedItems = itemSM.items.filter { $0.category == nil }
            if !uncategorizedItems.isEmpty {
                Section(header: Text("未分类")) {
                    ForEach(uncategorizedItems.sorted { $0.name ?? "" < $1.name ?? "" }) { item in
                        Text(item.name ?? "")
                    }
                }
            }
        }
        .onAppear{
           itemSM.loadItems(forList: selectedList)
       }
    }
}

方案二:服务层优化(性能更优)

直接从Core Data获取关联的Category并预加载对应Item,避免视图层的分组开销,适合数据量较大的场景。

  1. 修改ItemServiceModel,新增加载分类的方法:
class ItemServiceModel: ObservableObject{
    let manager: CoreDataManager
    @Published var categories: [Category] = []

    func loadCategories(forList list: List){
        let request = NSFetchRequest<Category>(entityName: "Category")
        
        // 过滤出包含当前List下Item的分类
        let predicate = NSPredicate(format: "ANY items.list == %@", list)
        request.predicate = predicate
        
        // 按分类名称排序
        let categorySort = NSSortDescriptor(keyPath: \Category.name, ascending: true)
        // 指定Item的排序规则
        let itemSort = NSSortDescriptor(keyPath: \Item.name, ascending: true)
        request.sortDescriptors = [categorySort]
        
        // 预加载关联的Item,避免Core Data懒加载的性能损耗
        request.relationshipKeyPathsForPrefetching = ["items"]
        
        do{
            categories = try manager.context.fetch(request)
            // 对每个分类下的Item应用排序
            categories.forEach { category in
                if let sortedItems = category.items?.sortedArray(using: [itemSort]) as? [Item] {
                    category.items = Set(sortedItems) as NSSet
                }
            }
        }catch let error{
            print("加载分类失败:\(error.localizedDescription)")
        }
    }
}
  1. 修改ItemsView,直接遍历分类和对应Item:
struct ItemsView: View {
    let selectedList: List

    @EnvironmentObject private var itemSM: ItemServiceModel
    
    var body: some View {
        List{
            ForEach(itemSM.categories) { category in
                Section(header: Text(category.name ?? "未分类")) {
                    ForEach(category.items?.allObjects as? [Item] ?? []) { item in
                        Text(item.name ?? "")
                    }
                }
            }
            
            // 可选:单独显示无分类的Item
            let uncategorizedItems = itemSM.items.filter { $0.category == nil }
            if !uncategorizedItems.isEmpty {
                Section(header: Text("未分类")) {
                    ForEach(uncategorizedItems.sorted { $0.name ?? "" < $1.name ?? "" }) { item in
                        Text(item.name ?? "")
                    }
                }
            }
        }
        .onAppear{
           itemSM.loadCategories(forList: selectedList)
       }
    }
}

内容的提问来源于stack exchange,提问作者fs_tigre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:05:55