4 Replies
      Latest reply on Apr 20, 2020 10:30 AM by TarqTeles
      cederacheBiB Level 1 Level 1 (0 points)

        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 ?

        • Re: SwiftUI TextField and voice dictation
          TarqTeles Level 1 Level 1 (0 points)

          I'm having the same problem here. It captures 2-3 letters, then disconnects...

            • Re: SwiftUI TextField and voice dictation
              cederacheBiB Level 1 Level 1 (0 points)

              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
              })
              
                • Re: SwiftUI TextField and voice dictation
                  TarqTeles Level 1 Level 1 (0 points)

                  Thank you for sharing, cederacheBiB

                    • Re: SwiftUI TextField and voice dictation
                      TarqTeles Level 1 Level 1 (0 points)

                      Hi, cederachBiB,

                       

                      Did a small change to your code that I'd like to share back. I was having problems with TextFieldWithFocus filling up too much space when I wanted a single-line TextField similar to SwiftUI's.

                      I tried adding a .frame(height: ) to my TextFieldWithFocus element, but it crashed the app. So I added this code to the end of the properties and beginning of the makeUIView(context:)

                       

                          var enablesReturnKeyAutomatically: Bool = false
                          @State var frameRect: CGRect = .zero
                        
                          var onCommit: (() -> Void)?
                          
                          func setFrameRect(_ rect: CGRect) -> some View {
                              self.frameRect = rect
                              
                              return self
                          }
                        
                          func makeUIView(context: UIViewRepresentableContext) -> UITextField {
                              let textField = UITextField(frame: self.frameRect)
                              textField.delegate = context.coordinator
                              textField.placeholder = NSLocalizedString(placeholder, comment: "")

                       

                      And by using a GeometryReader around the element (or elements in a HStack), it's now possbile to set the frame this way, for example:

                       

                      GeometryReader { geometry in
                          HStack {
                              TextFieldWithFocus(text: self.$newString, placeholder: "placeholder", isFirstResponder: self.$textHasFocus,
                                  onCommit: { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, 
                                  from: nil, for: nil)
                                  self.textHasFocus = false
                              })
                                  .setFrameRect(geometry.frame(in: .global))
                                  .frame(height: 36)
                                  .border(Color.black, width: 1)
                                  .padding(.horizontal)
                      ...
                      }

                      Hope this can be of use, and thanks again for sharing your fix to our problem!

                       

                      Best, Tarq