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

SwiftUI:更新TransactionEntity后如何刷新List内的Section?

解决SwiftUI List在CoreData实体更新后不刷新的问题

核心问题分析

  1. saveTransactions方法逻辑错误:当前代码每次调用都会创建新的TransactionEntity,而非更新原有记录,导致列表里的旧实体根本没被修改。
  2. 视图数据绑定不合理:HomeListView用@State存储transactions,即使onAppear重新获取,SwiftUI可能因数组引用未变或实体未触发更新信号而不刷新。
  3. 实体未遵循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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 11:48:21