使用 Combine + SwiftUI 在表单值更新时更新值

Using Combine + SwiftUI to update values when form values are updated

我正在学习 SwiftUI 和 Combine 来制作一个简单的分租应用程序。我试图遵循 MVVM 模式,因此有一个模型、视图模型和视图如下:

型号:

import Foundation
import Combine

struct Amounts {
    var myMonthlyIncome : String = ""
    var housemateMonthlyIncome : String = ""
    var totalRent : String = ""
}

视图模型:

import Foundation
import Combine

class FairRentViewModel : ObservableObject {
    
    var amount: Amounts
    
    init(_ amount: Amounts){
        self.amount = amount
    }
    
    var myMonthlyIncome : String { return amount.myMonthlyIncome }
    var housemateMonthlyIncome : String { return amount.housemateMonthlyIncome }
    var totalRent : String { return amount.totalRent }
    
    var yourShare: Double {
        guard let totalRent = Double(totalRent) else { return 0 }
        guard let myMonthlyIncome = Double(myMonthlyIncome) else { return 0 }
        guard let housemateMonthlyIncome = Double(housemateMonthlyIncome) else { return 0 }
        let totalIncome = Double(myMonthlyIncome + housemateMonthlyIncome)
        let percentage = myMonthlyIncome / totalIncome
        let value = Double(totalRent * percentage)

        return Double(round(100*value)/100)
    }
}

查看:


import SwiftUI
import Combine

struct FairRentView: View {
    
    @ObservedObject private var viewModel: FairRentViewModel
    
    init(viewModel: FairRentViewModel){
        self.viewModel = viewModel
    }
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Enter the total monthly rent:")) {
                    TextField("Total rent", text: $viewModel.amount.totalRent)
                        .keyboardType(.decimalPad)
                }
                
                Section(header: Text("Enter your monthly income:")) {
                    TextField("Your monthly wage", text: $viewModel.amount.myMonthlyIncome)
                        .keyboardType(.decimalPad)
                }
                
                Section(header: Text("Enter your housemate's monhtly income:")) {
                    TextField("Housemate's monthly income", text: $viewModel.amount.housemateMonthlyIncome)
                        .keyboardType(.decimalPad)
                }
                Section {
                    Text("Your share: £\(viewModel.yourShare, specifier: "%.2f")")
                }
            }
            .navigationBarTitle("FairRent")
        }
    }
}

struct FairRentView_Previews: PreviewProvider {
    static var previews: some View {
        FairRentView(viewModel: FairRentViewModel(Amounts()))
    }
}

入口点:

@main
struct FairRentCalculatorApp: App {
    var body: some Scene {
        WindowGroup {
            FairRentView(viewModel: FairRentViewModel(Amounts(myMonthlyIncome: "", housemateMonthlyIncome: "", totalRent: "")))
        }
    }
}

我希望 yourShare 值随着用户在表单中输入其他属性而更新。这就是我一直试图用上面的代码实现的。谁能帮我指出正确的方向?我是 SwiftUI + Combine 的新手,我正在尽我最大的努力编写干净的代码,因此也欢迎任何其他指示。

谢谢

您需要一些东西来向 SwiftUI 发出视图需要更新的信号。

ObservableObject 对象有两种方法可以做到这一点。一种是直接通过 self.objectWillChange 发布者,另一种更常见的是通过它的 @Published 属性,当更改时,会自动使用 objectWillChange

因此,对于您的情况,您只需将 amount 属性 标记为 @Published。因为它是 struct - value-type - 对其属性的任何更改也会更改整个对象:

@Published var amount: Amounts

因为计算的 属性 yourShare 只会在更新 amount 时更新,所以这会起作用。该视图将使用 now-updated yourShare.

重新计算自身