Как заставить TextField стать первым респондентом?

Вот мой код...

struct ContentView : View {
    @State var showingTextField = false
    @State var text = ""
    var body: some View {
        return VStack {
            if showingTextField {
                TextField($text)
            }
            Button(action: {
                self.showingTextField.toggle()
            }) {
                Text ("Show")
            }
        }
    }
}

То, что я хочу, - это когда текстовое поле становится видимым, чтобы текстовое поле стало первым респондентом (то есть получил фокус и поднял клавиатуру).

Ответ 1

На данный момент это не представляется возможным, но вы можете реализовать нечто подобное самостоятельно.

Вы можете создать собственное текстовое поле и добавить значение, чтобы оно стало первым респондентом.

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        var didBecomeFirstResponder = false

        init(text: Binding<String>) {
            _text = text
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            text = textField.text ?? ""
        }

    }

    @Binding var text: String
    var isFirstResponder: Bool = false

    func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        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
        }
    }
}

Примечание. didBecomeFirstResponder необходим, чтобы текстовое поле становилось первым респондентом только один раз, а не при каждом обновлении SwiftUI!

Вы бы использовали это так...

struct ContentView : View {

    @State var text: String = ""

    var body: some View {
        CustomTextField(text: $text, isFirstResponder: true)
            .frame(width: 300, height: 50)
            .background(Color.red)
    }

}

P.S. Я добавил frame, так как он не ведет себя как фондовый TextField, что означает, что за кулисами происходит больше вещей.

Подробнее о Coordinators в этом великолепном выступлении на WWDC 19: Интеграция SwiftUI

Ответ 2

Для всех, кто оказался здесь, но столкнулся с ошибкой при ответе @Matteo Pacini, учтите это изменение в бета-версии 4: Невозможно назначить свойству: & # 39; $ text & # 39; является неизменным для этого блока:

init(text: Binding<String>) {
    $text = text
}

следует использовать:

init(text: Binding<String>) {
    _text = text
}

И если вы хотите, чтобы текстовое поле стало первым респондентом в sheet, учтите, что вы не можете вызвать becomeFirstResponder, пока текстовое поле не отобразится. Другими словами, помещение текстового поля @Matteo Pacini непосредственно в содержимое sheet приводит к сбою.

Чтобы решить эту проблему, добавьте дополнительную проверку uiView.window != nil для видимости текстового поля. Фокус только после того, как он находится в иерархии представлений:

struct AutoFocusTextField: UIViewRepresentable {
    @Binding var text: String

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: UIViewRepresentableContext<AutoFocusTextField>) -> UITextField {
        let textField = UITextField()
        textField.delegate = context.coordinator
        return textField
    }

    func updateUIView(_ uiView: UITextField, context:
        UIViewRepresentableContext<AutoFocusTextField>) {
        uiView.text = text
        if uiView.window != nil, !uiView.isFirstResponder {
            uiView.becomeFirstResponder()
        }
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: AutoFocusTextField

        init(_ autoFocusTextField: AutoFocusTextField) {
            self.parent = autoFocusTextField
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
    }
}

Ответ 3

Вы можете попробовать использовать это:

TextField($text).focusable(true)