swiftxcodemodal-dialogtextfieldswiftui

Autofocus TextField programmatically in SwiftUI


I'm using a modal to add names to a list. When the modal is shown, I want to focus the TextField automatically, like this:

Preview

I've not found any suitable solutions yet.

Is there anything implemented into SwiftUI already in order to do this?

Thanks for your help.

var modal: some View {
        NavigationView{
            VStack{
                HStack{
                    Spacer()
                    TextField("Name", text: $inputText) // autofocus this!
                        .textFieldStyle(DefaultTextFieldStyle())
                        .padding()
                        .font(.system(size: 25))
                        // something like .focus() ??
                    Spacer()
                }
                Button(action: {
                    if self.inputText != ""{
                        self.players.append(Player(name: self.inputText))
                        self.inputText = ""
                        self.isModal = false
                    }
                }, label: {
                    HStack{
                        Text("Add \(inputText)")
                        Image(systemName: "plus")
                    }
                        .font(.system(size: 20))
                })
                    .padding()
                    .foregroundColor(.white)
                    .background(Color.blue)
                    .cornerRadius(10)
                Spacer()
            }
                .navigationBarTitle("New Player")
                .navigationBarItems(trailing: Button(action: {self.isModal=false}, label: {Text("Cancel").font(.system(size: 20))}))
                .padding()
        }
    }

Solution

  • iOS 15

    There is a new wrapper called @FocusState that controls the state of the keyboard and the focused keyboard ('aka' firstResponder).

    ⚠️ Note that if you want to make it focused at the initial time, you MUST apply a delay. It's a known bug of the SwiftUI.

    Become First Responder ( Focused )

    If you use a focused modifier on the text fields, you can make them become focused, for example, you can set the focusedField property in the code to make the binded textField become active:

    demo

    Resign first responder ( Dismiss keyboard )

    or dismiss the keyboard by setting the variable to nil:

    enter image description here

    Don't forget to watch the Direct and reflect focus in SwiftUI session from WWDC2021


    iOS 13 and 14 (and 15)

    Old but working:

    Simple wrapper struct - Works like a native:

    Note that Text binding support added as requested in the comments

    struct LegacyTextField: UIViewRepresentable {
        @Binding public var isFirstResponder: Bool
        @Binding public var text: String
    
        public var configuration = { (view: UITextField) in }
    
        public init(text: Binding<String>, isFirstResponder: Binding<Bool>, configuration: @escaping (UITextField) -> () = { _ in }) {
            self.configuration = configuration
            self._text = text
            self._isFirstResponder = isFirstResponder
        }
    
        public func makeUIView(context: Context) -> UITextField {
            let view = UITextField()
            view.addTarget(context.coordinator, action: #selector(Coordinator.textViewDidChange), for: .editingChanged)
            view.delegate = context.coordinator
            return view
        }
    
        public func updateUIView(_ uiView: UITextField, context: Context) {
            uiView.text = text
            switch isFirstResponder {
            case true: uiView.becomeFirstResponder()
            case false: uiView.resignFirstResponder()
            }
        }
    
        public func makeCoordinator() -> Coordinator {
            Coordinator($text, isFirstResponder: $isFirstResponder)
        }
    
        public class Coordinator: NSObject, UITextFieldDelegate {
            var text: Binding<String>
            var isFirstResponder: Binding<Bool>
    
            init(_ text: Binding<String>, isFirstResponder: Binding<Bool>) {
                self.text = text
                self.isFirstResponder = isFirstResponder
            }
    
            @objc public func textViewDidChange(_ textField: UITextField) {
                self.text.wrappedValue = textField.text ?? ""
            }
    
            public func textFieldDidBeginEditing(_ textField: UITextField) {
                self.isFirstResponder.wrappedValue = true
            }
    
            public func textFieldDidEndEditing(_ textField: UITextField) {
                self.isFirstResponder.wrappedValue = false
            }
        }
    }
    

    Usage:

    struct ContentView: View {
        @State var text = ""
        @State var isFirstResponder = false
    
        var body: some View {
            LegacyTextField(text: $text, isFirstResponder: $isFirstResponder)
        }
    }
    

    🎁 Bonus: Completely customizable

    LegacyTextField(text: $text, isFirstResponder: $isFirstResponder) {
        $0.textColor = .red
        $0.tintColor = .blue
    }