添加 ViewModel 后,SwiftUI 视图未在按钮切换上更新
Posted
技术标签:
【中文标题】添加 ViewModel 后,SwiftUI 视图未在按钮切换上更新【英文标题】:SwiftUI View Not Updating on Button Toggle After Adding ViewModel 【发布时间】:2021-05-28 13:15:05 【问题描述】:我正在为我的应用程序的用户制作一个注册表单。在表格中我有两个切换。一个是滑块,另一个是复选框。我能够让这两个切换正常工作,并且当我在视图中声明了所有变量时,视图将完美更新。
我已重构项目以将一些功能和错误检查移至 ViewModel。现在我的切换不再更新视图,但它们切换的布尔值正在改变值。
虽然我可以将所有内容都塞回视图中,但我的偏好是弄清楚为什么这不起作用以及解决这些问题的最佳方法是什么。例如,我不明白输入字段将如何更新视图模型,但切换不会。
这是我的 ViewModel:
import Foundation
class SignUpViewModel: ObservableObject
@Published
var email: String = ""
var companyName: String = ""
var username: String = ""
var firstName: String = ""
var lastName: String = ""
var password: String = ""
var passwordConfirmation: String = ""
var phoneNumber: String = ""
var isConsumer: Bool = true
var isChecked: Bool = false
var showSignUpErrors: Bool = false
var category: String
if isConsumer
return "consumer"
else
return "contractor"
var EmailisInvalid: Bool
if email.isEmpty || !email.contains("@")
return true
return false
var CompanyNameisInvalid: Bool
if companyName.isEmpty && !isConsumer
return true
return false
var PhoneNumberisInvalid: Bool
if phoneNumber.isEmpty || phoneNumber.count < 10
return true
return false
var PasswordLength: Int
return password.count
var PasswordConfirmationLength: Int
return passwordConfirmation.count
var PasswordLengthError: Bool
if password.count < 6 || password.isEmpty
return true
return false
var PasswordConfirmationError: Bool
if passwordConfirmation.isEmpty
return true
return false
var PasswordMatchError: Bool
if password != passwordConfirmation
return true
return false
var FormIsInvalid: Bool
if EmailisInvalid ||
CompanyNameisInvalid ||
PhoneNumberisInvalid ||
PasswordLengthError ||
PasswordConfirmationError ||
PasswordMatchError ||
!isChecked
return true
return false
这是我的观点:
import SwiftUI
import iPhoneNumberField
struct SignUpView: View
@ObservedObject var keyboardResponder = KeyboardResponder()
@StateObject var signupVM : SignUpViewModel
var body: some View
ScrollView
VStack
WelcomeLogo()
.frame(width: 50, height: 50, alignment: .center)
SignUpHeader(isConsumer: $signupVM.isConsumer)
AccountType(isConsumer: $signupVM.isConsumer)
MainFields(
email: $signupVM.email,
companyName: $signupVM.companyName,
username: $signupVM.username,
firstName: $signupVM.firstName,
lastName: $signupVM.lastName,
phoneNumber: $signupVM.phoneNumber,
password: $signupVM.password,
passwordConfirmation: $signupVM.passwordConfirmation,
isConsumer: $signupVM.isConsumer,
isChecked: $signupVM.isChecked
)
Spacer()
.frame(height: 20)
if signupVM.FormIsInvalid
Button(action:
signupVM.showSignUpErrors = true
)
SignUpButtonContent()
.background(Color.gray)
else
Button(action:
print("run the signup function")
)
SignUpButtonContent()
.background(Color("BrandOrange"))
.padding()
.offset(y: -keyboardResponder.currentHeight*0.4)
struct MainFields: View
@Binding var email: String
@Binding var companyName: String
@Binding var username: String
@Binding var firstName: String
@Binding var lastName: String
@Binding var phoneNumber: String
@Binding var password: String
@Binding var passwordConfirmation: String
@Binding var isConsumer: Bool
@Binding var isChecked: Bool
var body: some View
if !isConsumer
TextField("Company Name", text: $companyName)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
TextField("Email Address", text: $email)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
TextField("Username", text: $username)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
TextField("First Name", text: $firstName)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
TextField("Last Name", text: $lastName)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
iPhoneNumberField("Phone Number", text: $phoneNumber)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
SecureField("Password: Minimum 6 Characters", text: $password)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
SecureField("Password Confirmation", text: $passwordConfirmation)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
Button(action:
isChecked.toggle()
print(isChecked)
)
HStack
Image(systemName: isChecked ? "checkmark.square" : "square")
Text("By clicking the 'Sign Up' button you agree to comply with the Terms and Conditions as well as the Privacy Policy.")
.font(.body)
.foregroundColor(.primary)
struct SignUpHeader: View
@Binding var isConsumer: Bool
var body: some View
if isConsumer
Text("Sign Up as a Homeowner")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(.primary)
else
Text("Sign Up as a Contractor")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(.primary)
struct AccountType: View
@Binding var isConsumer: Bool
var body: some View
Toggle("Change Account Type", isOn: $isConsumer)
.font(.title2)
.toggleStyle(SwitchToggleStyle(tint: Color("BrandOrange")))
Spacer()
struct SignUpButtonContent: View
var body: some View
Text("Sign Up")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 250, height: 50)
.cornerRadius(10.0)
struct SignUpView_Previews: PreviewProvider
static var previews: some View
SignUpView(signupVM: SignUpViewModel())
所以这两个按钮:
Button(action:
isChecked.toggle()
print(isChecked)
)
HStack
Image(systemName: isChecked ? "checkmark.square" : "square")
Text("By clicking the 'Sign Up' button you agree to comply with the Terms and Conditions as well as the Privacy Policy.")
.font(.body)
.foregroundColor(.primary)
并且此按钮不再触发视图更新:
struct AccountType: View
@Binding var isConsumer: Bool
var body: some View
Toggle("Change Account Type", isOn: $isConsumer)
.font(.title2)
.toggleStyle(SwitchToggleStyle(tint: Color("BrandOrange")))
Spacer()
【问题讨论】:
出于好奇而提出的无关问题:从SignUpViewModel
中的间距来看,您的意思似乎是要发布所有属性。但据我所知,在 Swift 中,你这样做的方式只会使 email
发布。还是我错了?
@LinusGeffarth 好问题。我不确定。
【参考方案1】:
您必须将@Published
添加到要查看更改的每个变量。
@Published var email: String = ""
@Published var companyName: String = ""
@Published var username: String = ""
@Published var firstName: String = ""
@Published var lastName: String = ""
@Published var password: String = ""
@Published var passwordConfirmation: String = ""
@Published var phoneNumber: String = ""
@Published var isConsumer: Bool = true
@Published var isChecked: Bool = false
@Published var showSignUpErrors: Bool = false
也改变
@StateObject var keyboardResponder = KeyboardResponder()
@StateObject var signupVM : SignUpViewModel = SignUpViewModel()
it’s unsafe to create an observed object inside a view
【讨论】:
以上是关于添加 ViewModel 后,SwiftUI 视图未在按钮切换上更新的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:从 ViewModel 更改视图 @State 属性