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

SwiftUI与SwiftData中数据变更时总价无法自动更新的问题求助

SwiftUI与SwiftData中数据变更时总价无法自动更新的问题求助

我帮你梳理下当前代码的问题,以及对应的解决思路:

问题根源

  1. MenuCalculator的选中菜品未动态更新:你在MenuCalculator初始化时就一次性过滤出了selectedDishes并存在变量里,之后哪怕菜品的ischecked状态变化,这个变量也不会重新过滤,导致计算总价时用的还是初始化时的旧数据。
  2. 总价未自动关联数据变化:你用@State存储totalValue,只在初始化时赋值了一次,后续不管菜品状态还是折扣变化,除非手动调用onChange更新,否则不会自动刷新——但你只监听了折扣的变化,没监听菜品ischecked的变化。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:43:02