使用 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
.
重新计算自身
我正在学习 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
.