SwiftUI与SwiftData中数据变更时总价无法自动更新的问题求助
SwiftUI与SwiftData中数据变更时总价无法自动更新的问题求助
我帮你梳理下当前代码的问题,以及对应的解决思路:
问题根源
- MenuCalculator的选中菜品未动态更新:你在
MenuCalculator初始化时就一次性过滤出了selectedDishes并存在变量里,之后哪怕菜品的ischecked状态变化,这个变量也不会重新过滤,导致计算总价时用的还是初始化时的旧数据。 - 总价未自动关联数据变化:你用
@State存储totalValue,只在初始化时赋值了一次,后续不管菜品状态还是折扣变化,除非手动调用onChange更新,否则不会自动刷新——但你只监听了折扣的变化,没监听菜品ischecked的变化。 - MenuCalculator的生命周期问题:你用
@State持有MenuCalculator,但@State适合存储视图内部的简单状态,对于@Observable类型的对象,应该用@StateObject来管理,确保对象的状态变化能触发视图更新。
具体修改方案
1. 重构MenuCalculator,让它动态获取选中菜品
把selectedDishes从存储变量改成计算属性,这样每次计算总价时都会重新过滤最新的菜品状态:
@Observable public class MenuCalculator { var fullDiscount: Decimal private let allDishes: [Dish] // 持有所有菜品的引用,而非仅选中的 init(totalDiscount: Decimal, allDishes: [Dish]) { self.fullDiscount = totalDiscount self.allDishes = allDishes debugPrint("Filtered dishes count \(selectedDishes.count)") } // 动态过滤选中的菜品,每次调用都会重新计算 private var selectedDishes: [Dish] { allDishes.filter { $0.ischecked == true && $0.inmenu == true } } public func getTotalPrice() -> Decimal { debugPrint("Dishes for filter \(selectedDishes.count)") let allPrices = selectedDishes.map { dish in (1 - dish.discount/100) * (dish.priceperitem * dish.itemscount) } debugPrint(allPrices) return allPrices.reduce(0, +) } public func getTotalPriceWithDiscount() -> Decimal { debugPrint(" call get Discount") return (1 - fullDiscount/100) * getTotalPrice() } }
2. 修改CalculationView,让总价自动更新
- 用
@StateObject管理MenuCalculator,确保其状态变化能触发视图更新 - 把
totalValue改成计算属性,自动关联MenuCalculator的计算结果,无需手动维护 - 监听
dishes数组的变化,确保菜品列表更新时MenuCalculator能拿到最新数据
struct CalculationView: View { private let dishes: [Dish] @StateObject private var menuCalculator: MenuCalculator // 计算属性自动获取最新总价 private var totalValue: Decimal { menuCalculator.getTotalPriceWithDiscount() } init(dishes: [Dish]) { self.dishes = dishes debugPrint("Call reinit calculationView: firstitem is checked \(dishes[0].ischecked)") self._menuCalculator = StateObject(wrappedValue: MenuCalculator(totalDiscount: 0, allDishes: dishes)) } var body: some View { VStack{ LabeledContent("К оплате") { Text(totalValue, format: .currency(code: Locale.current.currency!.identifier) ) } LabeledContent("Скидка") { TextField("Цена за процию", value: $menuCalculator.fullDiscount, format: .number) .textFieldStyle(RoundedBorderTextFieldStyle()) .labelsHidden() .multilineTextAlignment(.center) .frame(width: 50) .fixedSize(horizontal: true, vertical: false) } } // 当菜品列表整体变化时,更新MenuCalculator .onChange(of: dishes) { newDishes in menuCalculator = MenuCalculator(totalDiscount: menuCalculator.fullDiscount, allDishes: newDishes) } } }
3. 确保Dish模型是可观察的
你的Dish是SwiftData模型,记得用@Model修饰,这样它的属性变化会被SwiftUI自动观察到:
@Model class Dish { var name: String var priceperitem: Decimal var ischecked: Bool var itemscount: Int var discount: Decimal var inmenu: Bool init(newName: String, newPrice: Decimal, newIsChecked: Bool, newItemsCount: Int) { self.name = newName self.priceperitem = newPrice self.ischecked = newIsChecked self.itemscount = newItemsCount self.discount = 0 // 设置默认值,可根据需求调整 self.inmenu = true // 设置默认值,可根据需求调整 } }
为什么这样修改能解决问题?
- 计算属性
selectedDishes会在每次调用时重新过滤菜品,确保拿到最新的ischecked状态 @StateObject管理的MenuCalculator会在其属性变化时触发视图更新,而totalValue作为计算属性会自动同步最新结果onChange(of: dishes)确保当菜品列表新增/删除菜品时,MenuCalculator能拿到最新的完整列表
内容来源于stack exchange
相关产品推荐
相关产品推荐

