SwiftUI中如何按Category分组Core Data的Item列表?
原代码的问题在于:
- 最初的实现遍历所有Item,每个Item生成独立的Section,导致同分类的Item分散在多个Section中
- 修改后的实现依然遍历每个Item生成Section,再在Section内显示同分类的所有Item,同一个分类会被重复生成多次Section
以下是两种可行的解决思路:
方案一:视图层直接分组处理
无需修改服务层,在视图内完成Item的分类分组,再生成对应Section。
- 让Core Data的
Category实体遵守Identifiable协议(利用Core Data对象自带的唯一objectID):
extension Category: Identifiable { public var id: ObjectIdentifier { objectID } }
- 修改
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,避免视图层的分组开销,适合数据量较大的场景。
- 修改
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)") } } }
- 修改
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
相关产品推荐
相关产品推荐

