I am trying to add an image into my textinput in swiftui without the use of a zstack because this cause the whole view to bug, this is the expected result , is there a way to add them without the use of a stack? or should i adapt the whole code based on it ?
HStack {
ZStack {
CustomTextField(placeholder: "Email", text: $email, textColor: .black, isValid: emailValid)
.focused($isEmailFocused)
if let isValid = emailValid {
Image(isValid ? "check_solid" : "cancel_solid")
.resizable()
.frame(width: 25, height: 25)
.foregroundColor(isValid ? Color("greenstrokes") : Color("redstrokes"))
.position(x: UIScreen.main.bounds.width - 100, y: 75)
}
}
}
While the overlay is the way to go for placing and aligning the image over the textfield, you should consider creating a view modifier for validation, so you can apply it to any textfield.
Otherwise, you will have repeated code and overlays everywhere, if you have multiple fields.
Here's a rough example that uses a view extension and a view modifier:
import SwiftUI
struct ValidateTextFieldView: View {
@State private var inputText: String = ""
@State private var inputNumber: Int?
var body: some View {
VStack(spacing: 10) {
TextField("Type yes to confirm", text: $inputText)
.padding()
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 8))
.validateTextField($inputText)
.textInputAutocapitalization(.never)
Text("Press Enter to validate field")
.font(.caption)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundStyle(.secondary)
.padding(.leading)
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(.background.secondary)
}
}
extension View {
func validateTextField(_ text: Binding<String>) -> some View {
self
.modifier(ValidateTextFieldModifier(text: text))
}
}
struct ValidateTextFieldModifier: ViewModifier {
//Parameters
@Binding var text: String
//State values
@State private var isValid: Bool = false
@State private var showValidation: Bool = false
@State private var validationColor: Color = .clear
//Body
func body(content: Content) -> some View {
content
.onChange(of: text) {
validationColor = .yellow
}
.onSubmit {
if text == "yes" { //add your own validation logic
validationColor = .green
}
else { //if not valid
validationColor = .red
}
}
.overlay(
RoundedRectangle(cornerRadius: 8) // Rounded rectangle with a border
.stroke(validationColor, lineWidth: 2)
)
.overlay(alignment: . trailing) {
Image(systemName: validationColor == .green ? "checkmark.circle.fill" : validationColor == .red ? "xmark.circle.fill" : "")
.foregroundStyle(validationColor)
.padding(.trailing)
}
}
}
#Preview {
ValidateTextFieldView()
}
I used a state to set the color depending on the validation result, but normally, you'd have a more refined method, like maybe using an enum for validation state, additional validation functions, maybe more parameters, etc.