【问题标题】:Using Combine + SwiftUI to update values when form values are updated表单值更新时使用 Combine + SwiftUI 来更新值
【发布时间】:2021-01-17 04:30:56
【问题描述】:

我正在学习 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 非常陌生,并且正在尽我最大的努力编写干净的代码,因此也欢迎任何其他指针。

谢谢

【问题讨论】:

    标签: swift mvvm swiftui combine


    【解决方案1】:

    您需要向 SwiftUI 发出需要更新视图的信号。

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

    因此,在您的情况下,您只需将amount 属性标记为@Published。因为它是 struct - 一个值类型 - 对其属性的任何更改也会更改整个对象:

    @Published var amount: Amounts
    

    因为计算属性 yourShare 仅在更新 amount 时才会更新,所以这将起作用。该视图将使用现在更新的yourShare 重新计算自身。

    【讨论】:

    • 感谢@NewDev - 这实际上没有用?但是,有效的是将这些属性标记为已发布:@Published var myMonthlyIncome : String @ Published var housemateMonthlyIncome : String @Published var totalRent : String
    • 您的代码与您发布的代码不同吗? myMonthlyIncomehousemateMonthlyIncome 是计算属性 - 您不能将属性包装器与计算属性一起使用
    猜你喜欢
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多