SwiftUI:更新TransactionEntity后如何刷新List内的Section?
解决SwiftUI List在CoreData实体更新后不刷新的问题
核心问题分析
- saveTransactions方法逻辑错误:当前代码每次调用都会创建新的
TransactionEntity,而非更新原有记录,导致列表里的旧实体根本没被修改。 - 视图数据绑定不合理:
HomeListView用@State存储transactions,即使onAppear重新获取,SwiftUI可能因数组引用未变或实体未触发更新信号而不刷新。 - 实体未遵循Identifiable:
TransactionEntity没实现Identifiable协议,ForEach无法精准识别实体的变化。
分步修复
1. 让TransactionEntity遵循Identifiable
修改TransactionEntity+CoreDataProperties扩展,添加协议遵循:
extension TransactionEntity: Identifiable { @nonobjc public class func fetchRequest() -> NSFetchRequest<TransactionEntity> { return NSFetchRequest<TransactionEntity>(entityName: "TransactionEntity") } @NSManaged public var amount: Double @NSManaged public var createdAt: Date? @NSManaged public var valueDate: Date? }
2. 修复RecordViewModel的saveTransactions方法(实现更新逻辑)
将新增实体的逻辑改为查找并更新原有实体,同时用@Published属性自动通知视图更新:
class RecordViewModel: ObservableObject { private let container: NSPersistentContainer // 用@Published存储交易列表,数据变化时自动通知视图 @Published var transactions: [TransactionEntity] = [] init(inMemory: Bool = false) { container = NSPersistentContainer(name: "MyExpense") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } // 初始化时加载数据 self.fetchAllTransactions() }) container.viewContext.automaticallyMergesChangesFromParent = true } func saveTransactions(record: RecordModel) { if let objectID = record.objectID { // 根据objectID查找已有实体并更新 if let transaction = container.viewContext.object(with: objectID) as? TransactionEntity { transaction.amount = record.amount transaction.createdAt = Date() transaction.valueDate = record.valueDate } } else { // 无objectID时新增实体 let entity = NSEntityDescription.entity(forEntityName: "TransactionEntity", in: container.viewContext)! let transaction = TransactionEntity(entity: entity, insertInto: container.viewContext) transaction.amount = record.amount transaction.createdAt = Date() transaction.valueDate = record.valueDate } do { try container.viewContext.save() // 保存后重新加载列表,确保数据最新 fetchAllTransactions() } catch let error as NSError { print("Could not save. \(error), \(error.userInfo)") } } func fetchAllTransactions() { let fetchRequest = TransactionEntity.fetchRequest() do { transactions = try container.viewContext.fetch(fetchRequest) } catch let error as NSError { print("Could not load. \(error), \(error.userInfo)") transactions = [] } } }
3. 优化HomeListView,直接绑定ViewModel的@Published数据
去掉本地@State存储的transactions,直接使用ViewModel的transactions属性,数据变化时自动触发刷新:
struct HomeListView: View { @StateObject var viewModel: RecordViewModel var categoryName: String var body: some View { List { Section( header: HStack { Text(categoryName) }, content: { ForEach(viewModel.transactions) { transaction in let model = RecordModel(transaction: transaction) NavigationLink( destination: RecordDetailsView(viewModel: viewModel, model: model), label: { HStack { Text(transaction.categoryName ?? "") // 注意:需确保CoreData实体有categoryName属性 Text("\(transaction.amount)") } }) } } ) } .onAppear { // 可选:每次视图出现时强制刷新数据 viewModel.fetchAllTransactions() } } }
4. 修正RecordDetailsView的参数与绑定逻辑
补充缺失的amountValue绑定逻辑,并用iOS15+推荐的dismiss替代presentationMode:
struct RecordDetailsView: View { @Environment(\.dismiss) var dismiss @ObservedObject var viewModel: RecordViewModel @State var model: RecordModel @State private var isAlertPresented = false // 补充amountValue的双向绑定逻辑 private var amountValue: Binding<String> { Binding( get: { "\(model.amount)" }, set: { model.amount = Double($0) ?? 0.0 } ) } var body: some View { Form { Section(header: HStack { Image(systemName: "dollarsign") Text("Amount") }) { TextField("", text: amountValue) .keyboardType(.decimalPad) } Section(header: HStack { Image(systemName: "calendar") Text("Date") }) { DatePicker( selection: $model.valueDate, in: ...Date(), displayedComponents: [.date, .hourAndMinute], label: { Text("") } ) .datePickerStyle(.wheel) } Section { Button("Save") { saveAction() } } } } func saveAction() { viewModel.saveTransactions(record: model) dismiss() } }
关键说明
@Published属性:ViewModel中的transactions用该修饰符后,数组内容变化时会自动发送更新通知,触发视图刷新。- 实体精准更新:通过
objectID定位原有实体,确保修改的是用户编辑的那条记录,而非创建新实体。 Identifiable协议:让CoreData实体遵循该协议后,ForEach能通过唯一ID识别实体变化,保证视图精准更新。
内容的提问来源于stack exchange,提问作者Poles
相关产品推荐
相关产品推荐

