我有一个SwiftUI视图类,该类能够更新其自己的Text视图,因为具有可绑定值的TextFields已由用户更新。问题在于所有变量都包含在View类本身中。一旦将变量提取到视图模型类中,但是由于可绑定值已更新,因此计算字段不再更新。这是(非更新)代码:
struct KeView: View {
var vm = KeViewModel()
var body: some View {
return VStack {
Image("ke")
InputFieldView(category: Localizable.weaponAp(), input: vm.$ap)
InputFieldView(category: Localizable.targetArmor(), input: vm.$targetArmor)
InputFieldView(category: Localizable.weaponRange(), input: vm.$weaponRange)
InputFieldView(category: Localizable.targetRange(), input: vm.$targetRange)
Text(vm.damageString)
.foregroundColor(Color.white)
.padding()
.background(vm.damageColor)
.frame(maxHeight: .infinity)
}
}
}
struct KeView_Previews: PreviewProvider {
static var previews: some View {
KeView()
}
}
struct KeViewModel {
@State var ap = ""
@State var targetArmor = ""
@State var targetRange = ""
@State var weaponRange = ""
var damageColor: Color {
if damageString.contains(Localizable.outOfRange()) { return Color.red }
if damageString.contains(Localizable.inefficient()) { return Color.black }
let d = damageString.split(separator: " ").last ?? ""
if (Double(d) ?? 0) < 10 { return Color.blue }
return Color.red
}
var damageString : String {
guard let ap = Double(ap),
let weaponRange = Double(weaponRange),
let targetRange = Double(targetRange),
let targetArmor = Double(targetArmor) else {
return Localizable.damagePrefix() + " 0"
}
if (weaponRange < targetRange){
return Localizable.outOfRange()
} else {
let difference = (weaponRange - targetRange) / 175
//print("Difference is equal to",difference)
let actualAp = ap + difference
//print("actual AP is equal to",actualAp)
if (actualAp < targetArmor){
return Localizable.inefficient()
} else if (targetArmor == 0){
return Localizable.damagePrefix()
+ "\(round(actualAp * 2))"
} else {
return Localizable.damagePrefix()
+ " \(round((actualAp - Double(targetArmor)) / 2 + 1.0))"
}
}
}
}
这是可以在用户输入值时更新的代码:
struct KeView: View {
@State var ap = ""
@State var targetArmor = ""
@State var targetRange = ""
@State var weaponRange = ""
var damageColor: Color {
if damageString.contains(Localizable.outOfRange()) { return Color.red }
if damageString.contains(Localizable.inefficient()) { return Color.black }
let d = damageString.split(separator: " ").last ?? ""
if (Double(d) ?? 0) < 10 { return Color.blue }
return Color.red
}
var damageString : String {
guard let ap = Double(ap),
let weaponRange = Double(weaponRange),
let targetRange = Double(targetRange),
let targetArmor = Double(targetArmor) else {
return Localizable.damagePrefix() + " 0"
}
if (weaponRange < targetRange){
return Localizable.outOfRange()
} else {
let difference = (weaponRange - targetRange) / 175
//print("Difference is equal to",difference)
let actualAp = ap + difference
//print("actual AP is equal to",actualAp)
if (actualAp < targetArmor){
return Localizable.inefficient()
} else if (targetArmor == 0){
return Localizable.damagePrefix()
+ "\(round(actualAp * 2))"
} else {
return Localizable.damagePrefix()
+ " \(round((actualAp - Double(targetArmor)) / 2 + 1.0))"
}
}
}
var body: some View {
return VStack {
Image("ke")
InputFieldView(category: Localizable.weaponAp(), input: $ap)
InputFieldView(category: Localizable.targetArmor(), input: $targetArmor)
InputFieldView(category: Localizable.weaponRange(), input: $weaponRange)
InputFieldView(category: Localizable.targetRange(), input: $targetRange)
Text(String(self.damageString))
.foregroundColor(Color.white)
.padding()
.background(damageColor)
.frame(maxHeight: .infinity)
}
}
}
struct KeView_Previews: PreviewProvider {
static var previews: some View {
KeView()
}
}
这似乎很愚蠢,因为我无法将变量提取到外部结构,而希望在数据和视图之间进行清晰的分离。任何帮助表示赞赏。最后,如果您想自己构建和运行项目,请访问https://github.com/jamesjmtaylor/wrd-ios
答案 0 :(得分:1)
将您的KeViewModel结构更改为满足ObservableObject协议的类。另外,将@State属性包装器更改为@Published属性包装器,如下所示:
class KeViewModel: ObservableObject {
@Published var ap = ""
@Published var targetArmor = ""
@Published var targetRange = ""
@Published var weaponRange = ""
还用@ObservedOjbect属性包装器标记ViewModel实例:
@ObservedObject var vm: KeViewModel
现在,您要通过TabView中的构造函数将此视图模型注入特定视图:
TabView {
KeView(vm: KeViewModel()).tabItem {
Text("KE")
Image("first")
...
并在“内容预览”中:
struct KeView_Previews: PreviewProvider {
static var previews: some View {
KeView(vm: KeViewModel())
}
}
现在,您的View可以观察ViewModel对象,以发布ViewModel对象属性的新值,而无需将其作为Environment Object向下提供给视图层次结构,但是仍然可以在所有必要的地方自动获取更新。
答案 1 :(得分:1)
这是您可能感兴趣的完整代码:
window.rootViewController = UIHostingController(rootView: KeView().environmentObject(KeViewModel())
class KeViewModel : ObservableObject {
@Published var ap = ""
@Published var targetArmor = ""
@Published var targetRange = ""
@Published var weaponRange = ""
var damageColor: Color {
if damageString.contains(Localizable.outOfRange()) { return Color.red }
if damageString.contains(Localizable.inefficient()) { return Color.black }
let d = damageString.split(separator: " ").last ?? ""
if (Double(d) ?? 0) < 10 { return Color.blue }
return Color.red
}
var damageString : String {
guard let ap = Double(ap),
let weaponRange = Double(weaponRange),
let targetRange = Double(targetRange),
let targetArmor = Double(targetArmor) else {
return Localizable.damagePrefix() + " 0"
}
if (weaponRange < targetRange){
return Localizable.outOfRange()
} else {
let difference = (weaponRange - targetRange) / 175
//print("Difference is equal to",difference)
let actualAp = ap + difference
//print("actual AP is equal to",actualAp)
if (actualAp < targetArmor){
return Localizable.inefficient()
} else if (targetArmor == 0){
return Localizable.damagePrefix()
+ "\(round(actualAp * 2))"
} else {
return Localizable.damagePrefix()
+ " \(round((actualAp - Double(targetArmor)) / 2 + 1.0))"
}
}
}
}
struct KeView: View {
@EnvironmentObject var model: KeViewModel
var body: some View {
return VStack {
Image("ke")
InputFieldView(category: Localizable.weaponAp(), input: $model.ap)
InputFieldView(category: Localizable.targetArmor(), input: $model.targetArmor)
InputFieldView(category: Localizable.weaponRange(), input: $model.weaponRange)
InputFieldView(category: Localizable.targetRange(), input: $model.targetRange)
Text(String(model.damageString))
.foregroundColor(Color.white)
.padding()
.background(model.damageColor)
.frame(maxHeight: .infinity)
}
}
}
该模型必须为class
,因为它需要符合observable
。所有变量都需要@published,这使事情变得更容易。