I have a popover that displays an error message to the user when input fields aren't filled out. What's happening is my validationMessage is properly being set, but when I go to show my popover, the initial time the popover appears, only a blank box shows.
If I input any field and display it again, it will properly render. Not sure what is causing this issue.
struct SignUpForm: View {
// General logic for validation
@State private var onboardingGeneralLogic = OnboardingGeneralLogic()
@Binding var selectedTab: String
@State var firstName = ""
@State var lastName = ""
@State var email: String
@State var password: String
@State var confirmPassword: String
@Binding var isGeneralFormComplete: Bool
@State private var validationMessage = ""
@State private var showValidationMessage = false
private var isFormValid: Bool {
!firstName.isEmpty &&
!lastName.isEmpty &&
onboardingGeneralLogic.isValidID(email) &&
onboardingGeneralLogic.doPasswordsMatch(password, confirmPassword)
}
private func checkGeneralFormCompletion() {
if isFormValid {
isGeneralFormComplete = true
showValidationMessage = false
} else {
isGeneralFormComplete = false
validationMessage = onboardingGeneralLogic.getErrorMessage(
firstName: firstName,
lastName: lastName,
email: email,
password: password,
confirmPassword: confirmPassword
)
showValidationMessage = !validationMessage.isEmpty
}
}
var body: some View {
VStack(spacing: 12) {
Text("General Information")
.font(Font.custom("Raleway-Regular", size: 20))
.padding(.top)
.padding(.bottom, 15)
TextField("First Name", text: $firstName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.bottom, 10)
TextField("Last Name", text: $lastName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.bottom, 10)
TextField("Email Address", text: $email)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.bottom, 10)
SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.bottom, 10)
SecureField("Confirm Password", text: $confirmPassword)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.bottom, 10)
Button(action: {
checkGeneralFormCompletion()
}) {
Text("Continue")
.font(Font.custom("Raleway-Regular", size: 20))
.foregroundColor(.white)
.padding([.leading, .trailing], 45)
.padding([.top, .bottom], 10)
.background( Color("PrimaryBlue"))
.cornerRadius(5)
}
.popover(isPresented: self.$showValidationMessage,
attachmentAnchor: .point(.top),
arrowEdge: .top,
content: {
VStack {
Text(validationMessage)
}
.multilineTextAlignment(.center)
.lineLimit(0)
.foregroundStyle(.black)
.font(.system(size: 18, weight: .semibold, design: .rounded))
.padding()
.presentationCompactAdaptation(.none)
.fixedSize(horizontal: false, vertical: true)
.frame(minWidth: 200)
})
.padding(.top, 70)
}
.padding()
}
}
struct OnboardingGeneralLogic: Codable {
func getErrorMessage(firstName: String, lastName: String, email: String, password: String, confirmPassword: String) -> String {
if firstName.isEmpty {
return "First name cannot be empty."
} else if lastName.isEmpty {
return "Last name cannot be empty."
} else if email.isEmpty || !email.contains("@") {
return "Invalid email address."
} else if password != confirmPassword {
return "Passwords do not match."
} else if password.count < 6 {
return "Password must be 6 characters long"
}
return "Please enter all fields"
}
}
Parent View
struct GetStartedMain: View {
@State private var selectedTab = "General" // Default tab
@State private var firstName = ""
@State private var lastName = ""
@State private var email = ""
@State private var password = ""
@State private var confirmPassword = ""
@State var isGeneralFormComplete = false
var body: some View {
ZStack { // Use ZStack to ensure background fills the entire view
Color("PrimaryGray") // Background color
.ignoresSafeArea() // Extend background to ignore safe area
VStack {
HStack {
TabButton(tabName: "General", selectedTab: $selectedTab)
Spacer()
TabButton(tabName: "Detailed", selectedTab: $selectedTab)
.disabled(!isGeneralFormComplete)
Spacer()
TabButton(tabName: "Picture", selectedTab: $selectedTab)
}
.padding(.horizontal)
.background(Color(.gray.opacity(0.1))) // Background for tabs
if selectedTab == "General" {
SignUpForm(
selectedTab: $selectedTab,
firstName: firstName,
lastName: lastName,
email: email,
password: password,
confirmPassword: confirmPassword,
isGeneralFormComplete: $isGeneralFormComplete
)
} else if selectedTab == "Detailed" && isGeneralFormComplete {
DetailedView()
} else if selectedTab == "Picture" {
ProfilePictureForm()
}
Spacer()
}
}
}
}
You need to capture the validationMessage
property in your pop over closure so that the latest version is available inside the closure
.popover(isPresented: self.$showValidationMessage,
attachmentAnchor: .point(.top),
arrowEdge: .top,
content: { [validationMessage] in //<-- here
//...
}