-1

在我学习 SwiftUI 的过程中,我真的在为这种 MVVM 架构而苦苦挣扎。话虽如此,我有一个项目,我一直在努力学习这一点。在其他人的帮助下,我已经走到了这一步。

当前的问题是,我需要使@Published封装在视图模型上的属性具有可选参数。不幸的是,我尝试的一切都不起作用。我要做的是获取用户在 TextFields 上输入的内容并将它们用于数学函数。

模型.swift

// Model.
protocol Height {
    var heightInCentimeters: Int { get }
}

struct ImperialHeight: Height {
    var feet: Int
    var inches: Int
    
    var heightInCentimeters: Int {
        Int(((Double(feet) * 12.0)) + Double(inches) * 2.54)
    }
}

struct MetricHeight: Height {
    var heightInCentimeters: Int
}

protocol Weight {
    var weightInKilograms: Int { get }
}

struct ImperialWeight: Weight {
    var pounds: Int
    
    var weightInKilograms: Int {
        Int(Double(pounds) * 0.4535924)
    }
}

struct MetricWeight {
    var weightInKilograms: Int
}
struct UserData {
    var age: Int = 18
    var weight: Weight
    var height: Height
}

查看模型

class UserDataViewModel: ObservableObject {
    
    @Published var userData: UserData = UserData(weight: ImperialWeight(pounds: 220), height: ImperialHeight(feet: 6, inches: 2))
...

如上所述,当前的问题是@Published属性包装器需要重量和高度。理想情况下,我想制作那些通过用户 TextField 输入填充该数据的选项。

我尝试阅读大量文档、教程和指南。不幸的是,我仍然无法掌握如何将其应用于我的项目的概念。

4

1 回答 1

0

我认为你有点过于复杂了。您可以使用它Measurement来为您完成大部分工作。如果您想以不同的方式编辑测量值,只需拥有单一的事实来源。下面是一个例子height

import SwiftUI
//The key for something like this is to save everything to one variable in this case I will use centimeters as the source of truth
struct Height{
    ///Source of truth
    var centimeters: Double = 0
    ///This will have a multitude of uses and has its on Formatter for when you want to display units based on locale, etc
    var heightInCentimeters: Measurement<UnitLength> {
        get{
            Measurement(value: centimeters, unit: .centimeters)
        }
        //A measurement of any UnitLength kind will be converted to cm
        //Make sure you use the given units and you dont create new ones or you will get zero
        set{
            centimeters = newValue.converted(to: .centimeters).value
        }
    }
    /// centimeters converted to feet using Swift.Measurement
    var feet: Double {
        get{
            return heightInCentimeters.converted(to: .feet).value
        }
        set{
            heightInCentimeters = Measurement(value: newValue, unit: UnitLength.feet)
        }
    }
    /// centimeters converted to feet and inches. They depend on each other like this
    var feetAndInches: (feet:Int, inches:Int){
        get{
            let feetDouble = heightInCentimeters.converted(to: .feet).value
            //figure out the decimal
            let feetForInches = feetDouble.truncatingRemainder(dividingBy: 1)
            //remove the decimal
            let feet = feetDouble - feetForInches
            //convert decimal to inches
            let inches = Measurement(value: feetForInches, unit: UnitLength.feet).converted(to: .inches).value
            //return whole numbers
            return (Int(feet), Int(inches))
        }
        set(newValue){
            let ftCM = Measurement(value: Double(newValue.feet), unit: UnitLength.feet).converted(to: .centimeters).value
            let inCM = Measurement(value: Double(newValue.inches), unit: UnitLength.inches).converted(to: .centimeters).value
            centimeters = ftCM + inCM
        }
    }
}

struct UserData {
    var age: Int = 18
    var height: Height
}

class UserDataViewModel: ObservableObject {
    
    @Published var userData: UserData = UserData(height: Height())
}
struct UserDataView: View {
    @StateObject var vm: UserDataViewModel = UserDataViewModel()
    var numFormatter: NumberFormatter{
        let format = NumberFormatter()
        format.maximumFractionDigits = 2
        return format
    }
    var body: some View {
        //Notice that TextField with formatter only "saves" the new value when the user presses return/done on the keyboard
        List{
            Section(header: Text("age"), content: {
                HStack{
                    Text("age")
                    TextField("age", value: $vm.userData.age, formatter: numFormatter)
                }
            })
            Section(header: Text("height = \(vm.userData.height.heightInCentimeters.description)"), content: {
                HStack{
                    Text("feet")
                    TextField("feet", value: $vm.userData.height.feet, formatter: numFormatter)
                }
                VStack{
                    HStack{
                        Text("feet")
                        TextField("feet", value: $vm.userData.height.feetAndInches.feet, formatter: numFormatter)
                    }
                    HStack{
                        Text("inches")
                        TextField("inches", value: $vm.userData.height.feetAndInches.inches, formatter: numFormatter)
                    }
                }
                HStack{
                    Text("centimeters")
                    TextField("centimeters", value: $vm.userData.height.centimeters, formatter: numFormatter)
                }
            })
        }
    }
}

struct UserDataView_Previews: PreviewProvider {
    static var previews: some View {
        UserDataView()
    }
}

这段代码可以是任意长度。不仅仅是身高。

于 2021-05-27T22:49:13.093 回答