This didn’t happen before I upgraded to Xcode 13. One of the TextFields in my view doesn’t show the text that’s in it until I select it. It’s there, you just can’t see it. The reason I know it’s there is that, if it weren’t, the Save button in the navigation bar would be disabled, but it’s not.
Here’s the code:
var body: some View
{
Form
{
TextField("Name", text: $name)
.modifier(TextFieldClearButton(text: $name))
.autocapitalization(.words)
if duplicateItem
{
Text("This name matches a different item.")
.foregroundColor(.red)
.font(.callout)
}
// Allow all dates, including earlier than today, in case the
// item being edited has already passed its due date.
DatePicker("Due Date:",
selection: $dueDate,
displayedComponents: .date)
.accentColor(.blue)
HStack
{
Spacer()
Button("Today")
{ dueDate = Date() }
.buttonStyle(DateButtonStyle())
Spacer()
Button("+ 1 month")
{ dueDate = Calendar.current.date(byAdding: .month, value: 1, to: dueDate )! }
.buttonStyle(DateButtonStyle())
Spacer()
Button("+ 1 year")
{ dueDate = Calendar.current.date(byAdding: .year, value: 1, to: dueDate )! }
.buttonStyle(DateButtonStyle())
Spacer()
}
HStack
{
Text("Price: $")
TextField("", text: $price)
.keyboardType(.decimalPad)
.modifier(TextFieldClearButton(text: $price))
}
if !priceIsValid
{
Text("If you include a price, it must be a valid number.")
.foregroundColor(.red)
.font(.callout)
}
Section(header: Text("Notes").foregroundColor(colorContrast))
{
TextEditor(text: $notes)
.frame(height: 100)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarTitle("Edit item", displayMode: .inline)
.navigationBarItems(
leading: Button("Cancel")
{ presentationMode.wrappedValue.dismiss() }
.foregroundColor(.blue), // *TEMP FIX*
trailing: Button("Save")
{ saveItem() }
.disabled(!formIsComplete)
.foregroundColor(formIsComplete ? .blue : .gray)) // *TEMP FIX*
.onAppear() { setup() }
}
It’s the first TextField that’s having the problem. All the other fields work fine, including the Price TextField.
I’ve tried putting the code inside a Section, adding an EmptyView above the field, adding a Text above the field, and deleting the modifiers.
Thanks, but this doesn’t look like the solution. The article specifically recommends not to use .focusable() on a TextField. In any case, I want to apply focus, not just make it focusable, which it already is.
I would like to report this to Apple, but my Feedback app disappeared with IOS15 and I can’t find it in the App Store.
I guess nobody reading this has seen this problem.
The other alternative is to create a UIViewRepresentable custom TextField in which you can make use of UIKit to set that field as the firstResponder(). The code below is an example of how to do that.
It creates a textField that mirrors the way a SwiftUI textfield looks with a style of RoundedBorderTextFieldStyle() including giving you an option to have placeholder text.
If you create a test project and replace the contents of ContentView with all of this code you can see how it works.
struct ContentView: View {
@State private var email = ""
@State private var password = ""
var body: some View {
VStack(spacing: 20) {
TextField("Email", text: $email)
.textFieldStyle(RoundedBorderTextFieldStyle())
CustomTextField(text: $password, placeHolderText: "Password")
.padding(5)
.frame(height: 40)
.overlay(RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray.opacity(0.5), lineWidth: 1)
)
}
.padding(.horizontal)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CustomTextField: UIViewRepresentable {
@Binding var text: String
var isFirstResponder: Bool = false
var placeHolderText = ""
func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
let textField = UITextField(frame: .zero)
textField.delegate = context.coordinator
textField.placeholder = placeHolderText
textField.becomeFirstResponder()
return textField
}
func makeCoordinator() -> CustomTextField.Coordinator {
return Coordinator(text: $text)
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
uiView.text = text
if isFirstResponder && !context.coordinator.didBecomeFirstResponder {
uiView.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
class Coordinator: NSObject, UITextFieldDelegate {
@Binding var text: String
var didBecomeFirstResponder = false
init(text: Binding<String>) {
_text = text
}
func textFieldDidChangeSelection(_ textField: UITextField) {
text = textField.text ?? ""
}
}
}
Note, the fact that you cannot use a setting to make the field automatically focused as you navigate to the View is not a bug. The SwiftUI developers have not made that a priority. I suspect that at some point there will be a modifier to enable you to do that.
When the view opens, the TextField is blank, even though the @State field is not empty. Tapping inside the field causes the text to appear. Before Xcode 13, the text appeared when the view opened.
Problem solved. I put all the fields except Notes inside a Vstack and now the text displays as it did with Xcode 12. I guess it should have been in a Vstack all along, but that never caused a problem until now.
I don’t know why that solved the problem, but oh well.
Another thing I noticed was that, after putting the fields inside a Vstack, the dividers between the fields were no longer there. I had to get them back with “Divider()”.