Вот мой SwiftUI код:

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")
            }
        }
    }
}

Я хочу, чтобы текстовое поле стало видимым , чтобы текстовое поле стало первым респондентом (т.е. получить фокус и всплывающую клавиатуру).

121
Epaga 8 Июн 2019 в 18:44

5 ответов

Используя https://github.com/timbersoftware/SwiftUI-Introspect, можно:

TextField("", text: $value)
.introspectTextField { textField in
    textField.becomeFirstResponder()
}

26
Joshua Kifer 10 Дек 2019 в 23:56

Простая структура-оболочка - работает как нативная:

struct ResponderTextField: UIViewRepresentable {

    typealias TheUIView = UITextField
    var isFirstResponder: Bool
    var configuration = { (view: TheUIView) in }

    func makeUIView(context: UIViewRepresentableContext<Self>) -> TheUIView { TheUIView() }
    func updateUIView(_ uiView: TheUIView, context: UIViewRepresentableContext<Self>) {
        _ = isFirstResponder ? uiView.becomeFirstResponder() : uiView.resignFirstResponder()
        configuration(uiView)
    }
}

Применение:

struct ContentView: View {
    @State private var isActive = false

    var body: some View {
        ResponderTextField(isFirstResponder: isActive)
        // Just set `isActive` anyway you like
    }
}

🎁 Бонус: полностью настраиваемый

ResponderTextField(isFirstResponder: isActive) {
    $0.textColor = .red
    $0.tintColor = .blue
}

Этот метод полностью адаптируем. Например, вы можете увидеть Как добавить индикатор активности в SwiftUI с помощью того же метода here

6
Mojtaba Hosseini 30 Ноя 2019 в 09:11

Как отметили другие (например, @kipelovets прокомментируйте принятый ответ, например, @ ответ Eonil), я также не нашел ни одного из принятых решений для работы на macOS. Однако мне повезло: я использовал NSViewControllerRepresentable, чтобы получить NSSearchField, чтобы отображаться в качестве первого респондента в представлении SwiftUI:

import Cocoa
import SwiftUI

class FirstResponderNSSearchFieldController: NSViewController {

  @Binding var text: String

  init(text: Binding<String>) {
    self._text = text
    super.init(nibName: nil, bundle: nil)
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override func loadView() {
    let searchField = NSSearchField()
    searchField.delegate = self
    self.view = searchField
  }

  override func viewDidAppear() {
    self.view.window?.makeFirstResponder(self.view)
  }
}

extension FirstResponderNSSearchFieldController: NSSearchFieldDelegate {

  func controlTextDidChange(_ obj: Notification) {
    if let textField = obj.object as? NSTextField {
      self.text = textField.stringValue
    }
  }
}

struct FirstResponderNSSearchFieldRepresentable: NSViewControllerRepresentable {

  @Binding var text: String

  func makeNSViewController(
    context: NSViewControllerRepresentableContext<FirstResponderNSSearchFieldRepresentable>
  ) -> FirstResponderNSSearchFieldController {
    return FirstResponderNSSearchFieldController(text: $text)
  }

  func updateNSViewController(
    _ nsViewController: FirstResponderNSSearchFieldController,
    context: NSViewControllerRepresentableContext<FirstResponderNSSearchFieldRepresentable>
  ) {
  }
}

Пример представления SwiftUI:

struct ContentView: View {

  @State private var text: String = ""

  var body: some View {
    FirstResponderNSSearchFieldRepresentable(text: $text)
  }
}

2
David Muller 21 Янв 2020 в 21:57

Поскольку цепочка ответов недоступна для использования через SwiftUI, поэтому мы должны использовать ее с помощью UIViewRepresentable.

Посмотрите на ссылку ниже, так как я нашел обходной путь, который может работать аналогично тому, как мы используем UIKit.

https://stackoverflow.com/a/61121199/6445871

0
Anshuman Singh 16 Апр 2020 в 13:41

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

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 ?? ""
        }
    }
}
13
Rui Ying 23 Авг 2019 в 02:09