below is the code snippet you can test, for me - print(text) always prints nothing and isFilled is false no matter how many times I type in something
import SwiftUI
struct TextInputField: View {
var title: String
var prompt: String
let validate: (String)->Bool
var placeholder: String
@Binding var text: String
@State var isEditing: Bool = false
@State var error: Bool = false
@State var isFilled: Bool = false
var body: some View {
ZStack(alignment: .leading) {
Text(placeholder)
.foregroundColor(text.isEmpty ? Color(.placeholderText) : .accentColor)
.offset(x: 0, y: (!isEditing && !isFilled) ? 0 : -14)
.padding()
.font(UIConstraints.fontRegular(size: (isEditing && !isFilled) ? 12 : 16))
TextField("", text: $text, onEditingChanged: { isEditing in
withAnimation(.default) {
self.isEditing = isEditing
}
print(text)
isFilled = !text.isEmpty
print(isFilled)
})
.font(UIConstraints.fontRegular(size: 16))
.offset(x: 0, y: (!isEditing && !isFilled) ? 0 : 6)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(isEditing ? Color.accentColor : Color(.secondarySystemBackground), lineWidth: 2)
)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(error ? Color.red : Color.clear, lineWidth: 2)
)
}
.frame(height: 56)
.background {
Color(.secondarySystemBackground)
.cornerRadius(5.0)
.shadow(radius: 5.0)
}
.animation(.default, value: error)
if error {
Text(prompt)
.padding(.leading, 2)
.font(.footnote)
.foregroundColor(Color(.systemRed))
}
}
}
struct InputTextField_Previews: PreviewProvider {
@State static var text: String = ""
@State static var valid: Bool = true
static var previews: some View {
TextInputField(title: "", prompt: "promt", validate: {$0.isEmpty}, placeholder: "placeholder", text: $text)
.padding()
}
}
please help T_T
**Adding more text to be allowed to post the question: this is a custom textField to add a floating placeholder with animations. It's not done yet and some parts are unused yet, but I don't think its crucial for this bug
There are several issues with the provided code.
To start with, as mentioned in the comments, onEditingChanged
does not work as you'd expect. Instead, use the onChange(of:perform:)
function instead (or the newer iOS 17 variant if possible). You could for instance add it to your TextField
:
TextField("", text: $text, onEditingChanged: { isEditing in
withAnimation(.default) {
self.isEditing = isEditing
}
})
.onChange(of: text, perform: { text in
print(text)
isFilled = !text.isEmpty
print(isFilled)
})
Secondly, the isFilled
is currently a State
variable, but this can easily be transformed into a computed property instead. You'll have to update the view slightly to re-enable the animation:
Replace the @State var isFilled: Bool = false
private var isFilled: Bool {
!text.isEmpty
}
Next add the animation for the isFilled
property. You already had one for the error, place this one below it:
.animation(.default, value: isFilled)
Since in your code the onEditingChanged
callback is only used to set the isFilled
property, I believe you could now even omit the onChange(of:perform:)
function, as behaviour is working without it.
And finally the preview code. Now using static
State
properties has never really worked for me. If you need to have some state during preview, either see if you can mock it using the .constant(<value>)
when possible, to generate static examples:
struct InputTextField_Previews: PreviewProvider {
static var previews: some View {
TextInputField(
title: "Title",
prompt: "Prompt",
validate: { $0.isEmpty },
placeholder: "Placeholder",
text: .constant("testvalue") // <-- This line here
)
.padding()
}
}
This does not make your preview "interactive". So you won't be able to see the animations at work. Your best shot to get this to work, is to use an intermediate view instead. Often times, I create a "Wrapper" view to accomplish just that:
struct InputTextField_Previews: PreviewProvider {
struct Wrapper: View {
@State private var text: String = ""
@State private var valid: Bool = true
var body: some View {
TextInputField(
title: "",
prompt: "promt",
validate: {$0.isEmpty},
placeholder: "placeholder",
text: $text
)
.padding()
}
}
static var previews: some View {
Wrapper()
}
}
A combination of all these fixes will likely resolve your issue too!