I'm trying to make a custom TextField which eventually show errors when loosing focus in a normal form, foreach TextField, I write:
TextField(label, text: $text)
.focused($checkoutInFocus, equals: <some Value>)
Text(textError)
.foregroundColor(.red)
.frame(height: textError == "" ? 0 : 20)
(I want to precise it's not all: I have many modifiers for the TextField and it becomes very difficult to read when there are many TextFields)
So I thought it would be a good idea to make a special view for each Textfield:
struct TextFieldWithError: View {
var label: String
@Binding var text: String
@Binding var textError: String
@FocusState.Binding var isFocused: Bool
init(label: String, text: Binding<String>, textError: Binding<String>, isFocused: FocusState<Bool>.Binding) {
self.label = label
self._text = text
self._textError = textError
self._isFocused = isFocused
}
var body: some View {
TextField(label, text: $text)
.modifier(TextFieldStyle())
.focused($isFocused)
Text(textError)
.foregroundColor(.red)
.frame(height: textError == "" ? 0 : 20)
}
}
My problem is that I don't find the way to act on the focusState.
In the mainView I write:
TextFieldWithError(label: "Email", text: $email, textError: $emailError, isFocused: checkoutInFocus == .<some Value>)
in place of the modifier
.focused($checkoutInFocus, equals: .email)
but the syntax is obviously not good: I get the error:
Cannot convert value of type 'Bool' to expected argument type 'FocusState<Bool>.Binding'
Do you think there is a way?
I can send it with pleasure, by mail because code is very dense, but I think I found what was wrong:
Apple says about @FocusState:
A property wrapper type that can read and write a value that SwiftUI updates as the placement of focus within the scene changes.
to use this FocusState property, I used an enum declaration which was contained in the main view. But if I want to make a TextfieldWithError view, which use this enum, I have to declare this enum alone, not in my main View:
enum LoginFocusable: Hashable {
case email
case pwd
case none
var string: String {
switch self {
case .email: return "email"
case .pwd: return "pwd"
default: return ""
}
}
var nextToVerif: LoginFocusable {
switch self {
case .email: return .pwd
case .pwd: return .email
default: return .none
}
}
}
that's the first point
So, in TextfieldInError, I can have the declaration:
struct TextFieldWithError: View {
var label: String
@Binding var text: String
@Binding var textError: String
@FocusState.Binding var focus: LoginFocusable?
}
But t's not enough, because I will need to use the .focused modifier in TextFieldWithError so, I have to declare another property in TextFieldWithError: focusId of type LoginFocusable, so I will be able to user the .focused modifier
So my code will be
struct TextFieldWithError: View {
var label: String
@Binding var text: String
@Binding var textError: String
var focusId: LoginFocusable?
@FocusState.Binding var focus: LoginFocusable?
var body: some View {
TextField(label, text: $text)
.modifier(TextFieldStyle())
.focused($focus, equals: focusId)
Text(textError)
.foregroundColor(.red)
.frame(height: textError == "" ? 0 : 20)
}
}
Now, in the main view, I can declare:
TextFieldWithError(label: "Email", text: $email, textError: $emailError, focusId: .email, focus: $checkoutInFocus)
Of course, the logic of focus handle has to be written in this main View