SwiftUI Table在函数返回数据时不刷新的解决方法求助
解决方法
1. 改用@FetchRequest获取Core Data数据,自动监听变化
直接调用函数返回静态数组的方式,无法让SwiftUI感知Core Data的增删改变化。我们需要用@FetchRequest来绑定数据,它会自动监听Core Data上下文的变更并刷新View。
修改AccountView的初始化逻辑,构建对应的Fetch请求:
struct AccountView: View { let account: Account let month: Int let year: Int @State var selectedTransactionEntry: TransactionEntry.ID? @FetchRequest private var entries: FetchedResults<TransactionEntry> init(account: Account, month: Int, year: Int) { self.account = account self.month = month self.year = year // 构建当月的日期范围:当月第一天到下月第一天 let calendar = Calendar.current guard let startDate = calendar.date(from: DateComponents(year: year, month: month, day: 1)), let endDate = calendar.date(byAdding: .month, value: 1, to: startDate) else { fatalError("无效的日期参数") } // 构建筛选条件:指定账户 + 交易日期在当月范围内 let predicate = NSPredicate( format: "account == %@ AND transaction.date >= %@ AND transaction.date < %@", account, startDate as CVarArg, endDate as CVarArg ) // 按交易日期升序排序,保证累计余额计算顺序正确 let sortDescriptor = NSSortDescriptor(keyPath: \TransactionEntry.transaction?.date, ascending: true) _entries = FetchRequest( entity: TransactionEntry.entity(), sortDescriptors: [sortDescriptor], predicate: predicate ) } // 剩余代码... }
2. 用包装结构体计算累计余额,避免修改Core Data实体
原代码直接修改TransactionEntry.total(Core Data实体属性)的做法不合理,累计余额是派生值,不需要持久化到数据库。我们可以创建一个包装结构体,在运行时动态计算累计值:
// 包装结构体,包含原始交易条目和计算后的累计余额 struct TransactionEntryWithTotal: Identifiable { let entry: TransactionEntry let total: Double var id: TransactionEntry.ID { entry.id } }
在AccountView中添加计算属性,基于@FetchRequest的结果计算每个条目的累计余额:
private var entriesWithTotal: [TransactionEntryWithTotal] { // 获取账户当月初始余额 var runningTotal = fetchBalance(account: account, month: month, year: year) return entries.map { entry in // 根据收支类型更新累计余额 if entry.credit { runningTotal += entry.amount } else { runningTotal -= entry.amount } return TransactionEntryWithTotal(entry: entry, total: runningTotal) } } // 确保fetchBalance函数可在View中访问(根据实际情况调整作用域) private func fetchBalance(account: Account, month: Int, year: Int) -> Double { // 原fetchBalance的实现逻辑 }
3. 更新Table使用计算后的数据源
将Table的数据源替换为entriesWithTotal,并调整列的内容访问方式:
var body: some View { Table(entriesWithTotal, selection: $selectedTransactionEntry) { TableColumn("Date") { item in Text(DateTool.formatter.string(from: item.entry.transaction!.date!)) } TableColumn("Description") { item in Text(item.entry.transaction!.details!) } TableColumn("Category") { item in Text(item.entry.account!.name!) } TableColumn("Credit") { item in if item.entry.credit { Text("\(item.entry.amount as NSNumber, formatter: DoubleTool.formatter)") } } TableColumn("Debit") { item in if !item.entry.credit { Text("\(item.entry.amount as NSNumber, formatter: DoubleTool.formatter)") } } TableColumn("Total") { item in Text("\(item.total as NSNumber, formatter: DoubleTool.formatter)") } } .frame(height: 210) }
4. 移除原fetchTransactionEntry函数
现在@FetchRequest已经负责数据获取和变更监听,原有的fetchTransactionEntry函数可以删除。
关键说明
@FetchRequest会自动监听Core Data上下文的变化,当交易条目被增删改时,entries会自动更新,触发entriesWithTotal重新计算,进而刷新Table。- 用包装结构体存储累计余额,避免修改Core Data实体,保证原始交易数据的单一来源,防止数据不一致问题。
内容的提问来源于stack exchange,提问作者Jean-Charles
相关产品推荐
相关产品推荐

