これが私の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")
}
}
}
}
テキストフィールドがvisibleになると、テキストフィールドが最初のレスポンダーになります(つまり、フォーカスを受け取り、キーボードをポップアップします)。
現時点では可能ではないようですが、同様のものを自分で実装できます。
カスタムテキストフィールドを作成し、値を追加して、最初のレスポンダーにすることができます。
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)
}
}
追伸frame
は、在庫のTextField
のように動作しないため、追加で追加しました。
この優れたWWDC 19の講演でのCoordinators
の詳細: SwiftUIの統合
Xcode 11.1でテスト済み
https://github.com/timbersoftware/SwiftUI-Introspect を使用すると、次のことができます。
TextField("", text: $value)
.introspectTextField { textField in
textField.becomeFirstResponder()
}
ここに到達したが、@ 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 ?? ""
}
}
}
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にアクティビティインジケーターを追加する方法 を確認できます ここ
他の人が指摘しているように(例 @ 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)
}
}
選択した回答により、AppKitで無限ループの問題が発生します。 UIKitケースについては知りません。
この問題を回避するには、NSTextField
インスタンスを直接共有することをお勧めします。
import AppKit
import SwiftUI
struct Sample1: NSViewRepresentable {
var textField: NSTextField
func makeNSView(context:NSViewRepresentableContext<Sample1>) -> NSView { textField }
func updateNSView(_ x:NSView, context:NSViewRepresentableContext<Sample1>) {}
}
こんな風に使えます。
let win = NSWindow()
let txt = NSTextField()
win.setIsVisible(true)
win.setContentSize(NSSize(width: 256, height: 256))
win.center()
win.contentView = NSHostingView(rootView: Sample1(textField: txt))
win.makeFirstResponder(txt)
let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.run()
これは純粋な値のセマンティックを壊しますが、AppKitに依存することは、純粋な値のセマンティックを部分的に放棄し、ある程度の汚れを与えることを意味します。これは、SwiftUIでのファーストレスポンダーコントロールの欠如に対処するために今必要な魔法の穴です。
NSTextField
に直接アクセスするので、first-responderの設定は単純なAppKitの方法なので、目に見えるトラブルの原因はありません。
動作するソースコード ここ をダウンロードできます。