【问题标题】:SwiftUI View Not Updating on Button Toggle After Adding ViewModel添加 ViewModel 后,SwiftUI 视图未在按钮切换上更新
【发布时间】: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 好问题。我不确定。

标签: swift swiftui


【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2023-03-07
    • 2020-06-13
    相关资源
    最近更新 更多