Hi everyone,
I'm building an app with some TextField and I have some issues using the keyboard voice dictation.
I created a SingleView project with a single TextField in the ContentView.
struct ContentView: View {
@State var text: String = ""
var body: some View {
TextField("Description", text: $text)
.padding()
}
}
When editing this TextField in the app, if I click on the mic button (in my keyboard), I only receive the first letter in the TextField.
Is it a SwiftUI bug ? Like a view refresh breaking the link between the @State var and the voice dictation content ?
I solved this bug by using a TextField from UIKit in a UIViewRepresentable.
Here is my script of a focusable TextField :
import Combine
import SwiftUI
struct TextFieldWithFocus: UIViewRepresentable {
class Coordinator: NSObject, UITextFieldDelegate {
@Binding var text: String
@Binding var isFirstResponder: Bool
var didBecomeFirstResponder = false
var onCommit: () -> Void
init(text: Binding<String>, isFirstResponder: Binding<Bool>, onCommit: @escaping () -> Void) {
_text = text
_isFirstResponder = isFirstResponder
self.onCommit = onCommit
}
func textFieldDidChangeSelection(_ textField: UITextField) {
text = textField.text ?? ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
isFirstResponder = false
didBecomeFirstResponder = false
onCommit()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
isFirstResponder = false
didBecomeFirstResponder = false
}
func textFieldDidBeginEditing(_ textField: UITextField) {
isFirstResponder = true
}
}
@Binding var text: String
var placeholder: String
@Binding var isFirstResponder: Bool
var textAlignment: NSTextAlignment = .left
var isSecure: Bool = false
var keyboardType: UIKeyboardType = .default
var returnKeyType: UIReturnKeyType = .default
var textContentType: UITextContentType?
var textFieldBorderStyle: UITextField.BorderStyle = .none
var enablesReturnKeyAutomatically: Bool = false
var onCommit: (() -> Void)?
func makeUIView(context: UIViewRepresentableContext<TextFieldWithFocus>) -> UITextField {
let textField = UITextField(frame: .zero)
textField.delegate = context.coordinator
textField.placeholder = NSLocalizedString(placeholder, comment: "")
textField.textAlignment = textAlignment
textField.isSecureTextEntry = isSecure
textField.keyboardType = keyboardType
textField.returnKeyType = returnKeyType
textField.textContentType = textContentType
textField.borderStyle = textFieldBorderStyle
textField.enablesReturnKeyAutomatically = enablesReturnKeyAutomatically
return textField
}
func makeCoordinator() -> TextFieldWithFocus.Coordinator {
return Coordinator(text: $text, isFirstResponder: $isFirstResponder, onCommit: {
self.onCommit?()
})
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldWithFocus>) {
uiView.text = text
if isFirstResponder && !context.coordinator.didBecomeFirstResponder {
uiView.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
}
struct TextFieldWithFocus_Previews: PreviewProvider {
static var previews: some View {
TextFieldWithFocus(text: .constant(""), placeholder: "placeholder", isFirstResponder: .constant(false))
}
}
Just call it that way from SwiftUI side :
TextFieldWithFocus(text: $title,
placeholder: NSLocalizedString("summary", comment: ""), isFirstResponder: $titleFocused, onCommit: {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
self.titleFocused = false
})