Swift 迅捷+;结合使用模型和ViewModels
我是Swift的新手,目前正在尝试使用SwiftUI+Combine构建一个租金分摊应用程序。我希望遵循MVVM模式,并尝试实现这一点。目前,我有以下模型、视图模型和视图文件: 型号:Swift 迅捷+;结合使用模型和ViewModels,swift,mvvm,swiftui,combine,Swift,Mvvm,Swiftui,Combine,我是Swift的新手,目前正在尝试使用SwiftUI+Combine构建一个租金分摊应用程序。我希望遵循MVVM模式,并尝试实现这一点。目前,我有以下模型、视图模型和视图文件: 型号: import Foundation import Combine struct InputAmounts { var myMonthlyIncome : Double var housemateMonthlyIncome : Double var totalRent : Double }
import Foundation
import Combine
struct InputAmounts {
var myMonthlyIncome : Double
var housemateMonthlyIncome : Double
var totalRent : Double
}
ViewModel(我试图使用模型中的数据来符合MVVM模式,但我不确定我是否以最干净的方式/正确的方式进行了此操作,因此如果错误,请纠正我)
然后我试着将这一切传递给视图:
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.totalRent)
.keyboardType(.decimalPad)
}
Section(header: Text("Enter your monthly income:")) {
TextField("Your monthly wage", text: $viewModel.myMonthlyIncome)
.keyboardType(.decimalPad)
}
Section(header: Text("Enter your housemate's monthly income:")) {
TextField("Housemate's monthly income", text: $viewModel.housemateMonthlyIncome)
.keyboardType(.decimalPad)
}
Section {
Text("Your share: £\(viewModel.yourShare, specifier: "%.2f")")
}
}
.navigationBarTitle("FairRent")
}
}
}
struct FairRentView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = FairRentViewModel(inputAmounts: <#InputAmounts#>)
FairRentView(viewModel: viewModel)
}
}
我在这里得到了一个错误,“可选类型'Double'的值必须被解包为'Double'类型的值”,我以为我是用??操作数?视图模型应具有要在视图中使用的每个模型属性的属性,并且视图模型还应负责将它们转换为适合视图的格式。属性应标记为@Published to,以便在更改时更新视图 比如说
@Published var myMonthlyIncome: String
这是一个字符串,我们可以在init
myMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.myMonthlyIncome) ?? ""
这是我的视图模型的完整版本
final class FairRentViewModel : ObservableObject {
private static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
private var inputAmounts: InputAmounts
@Published var myMonthlyIncome: String
@Published var housemateMonthlyIncome: String
@Published var totalRent: String
init(inputAmounts: InputAmounts) {
self.inputAmounts = inputAmounts
myMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.myMonthlyIncome) ?? ""
housemateMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.housemateMonthlyIncome) ?? ""
totalRent = FairRentViewModel.formatter.string(for: inputAmounts.totalRent) ?? ""
}
var yourShare: String {
let value = inputAmounts.totalRent * inputAmounts.myMonthlyIncome / (inputAmounts.myMonthlyIncome + inputAmounts.housemateMonthlyIncome)
return FairRentViewModel.formatter.string(for: Double(round(100*value)/100)) ?? ""
}
func save() {
inputAmounts.myMonthlyIncome = FairRentViewModel.formatter.number(from: myMonthlyIncome)?.doubleValue ?? 0
//...
}
}
您应该为yourShare
执行类似的操作,save
方法只是一个简单的示例,说明了如果将函数绑定到按钮操作或类似操作,如何从视图更新模型
还要注意,我用于格式化程序的数字样式只是一个猜测,您可能需要更改它。在处理金钱时,建议使用十进制而不是双精度。似乎您正试图访问
$viewModel.totalRent
,但您的viewModel
没有任何名为totalRent
的属性。我认为您需要的可能是$viewModel.inputAmounts.totalRent
,如果您这样做,您必须从viewModel中的inputAmounts中删除private
,首先,没有必要将Double转换为Double,也永远不要执行??0
在进行分割时。谢谢@JoakimDanielson-这是否意味着我不需要仅为模型创建单独的文件?这真的取决于您,但就个人而言,我更喜欢为我的每个模型(以及视图模型和视图)创建单独的文件谢谢@JoakimDanielson。“为yourShare
做点类似的事情”是什么意思?请将其转换为字符串而不是double对不起@JoakimDanielson,我仍然不知道如何做-请您更新您的答案,以举例说明您的意思好吗?
myMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.myMonthlyIncome) ?? ""
final class FairRentViewModel : ObservableObject {
private static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
private var inputAmounts: InputAmounts
@Published var myMonthlyIncome: String
@Published var housemateMonthlyIncome: String
@Published var totalRent: String
init(inputAmounts: InputAmounts) {
self.inputAmounts = inputAmounts
myMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.myMonthlyIncome) ?? ""
housemateMonthlyIncome = FairRentViewModel.formatter.string(for: inputAmounts.housemateMonthlyIncome) ?? ""
totalRent = FairRentViewModel.formatter.string(for: inputAmounts.totalRent) ?? ""
}
var yourShare: String {
let value = inputAmounts.totalRent * inputAmounts.myMonthlyIncome / (inputAmounts.myMonthlyIncome + inputAmounts.housemateMonthlyIncome)
return FairRentViewModel.formatter.string(for: Double(round(100*value)/100)) ?? ""
}
func save() {
inputAmounts.myMonthlyIncome = FairRentViewModel.formatter.number(from: myMonthlyIncome)?.doubleValue ?? 0
//...
}
}