In the AppKit example below I want the first Textfield to have the (keyboard)-focus when the popover appears.
How can I
struct ContentView: View {
@State var showPopup = false
@State var text = ""
var body: some View {
VStack{
Text("Hello, world!")
.padding()
Button("show Popup") { showPopup = true}
}
.frame(width: 300, height: 300)
.padding()
.popover( isPresented: $showPopup,
arrowEdge: .trailing
) {
VStack{
Text("Popup")
TextField("enter Text",text:$text )
}
.padding()
}
}
}
I needed to do this in one of my projects myself. First, I subclassed NSViewController in which the view becomes the first responder (focused) when the view appears.
class FocusedTextFieldViewController: NSViewController, NSTextFieldDelegate {
@Binding var text: String
init(text: Binding<String>) {
_text = text
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError()
}
var textField: NSTextField!
override func loadView() {
// Set up the text field.
textField = NSTextField()
// Set the text field's delegate
textField.delegate = self
// Set an initial text value.
textField.stringValue = text
// Set the view to the new text field.
view = textField
}
override func viewDidAppear() {
// Make the view the first responder.
view.window?.makeFirstResponder(view)
}
func controlTextDidChange(_ obj: Notification) {
// Update the text.
text = textField.stringValue
}
}
Then, I used NSViewControllerRepresentable to display the NSViewController in SwiftUI.
struct FocusedTextField: NSViewControllerRepresentable {
@Binding var text: String
func makeNSViewController(context: Context) -> some FocusedTextFieldViewController {
return FocusedTextFieldViewController(text: $text)
}
func updateNSViewController(_ nsViewController: NSViewControllerType, context: Context) {
let textField = nsViewController.textField
textField?.stringValue = text
}
}
Now, you can use the "FocusedTextField" in SwiftUI.
struct ContentView: View {
@State private var popoverIsPresented = false
@State private var text = ""
var body: some View {
Button("Show Popover") {
// Present the popover.
popoverIsPresented.toggle()
}
.popover(isPresented: $popoverIsPresented, content: {
FocusedTextField(text: $text)
})
}
}
If you want to unfocus the text field, you could add this to the loadView function to the FocusedTextFieldViewController and use Notification Center for unfocusing the text field again.
override func loadView() {
// Set up the text field.
textField = NSTextField()
// Set the text field's delegate
textField.delegate = self
// Set an initial text value.
textField.stringValue = text
// Set the view to the new text field.
view = textField
// Set up a notification for unfocusing the text field.
NotificationCenter.default.addObserver(forName: Notification.Name("UnfocusTextField"), object: nil, queue: nil, using: { _ in
// Unfocus the text field.
self.view.window?.resignFirstResponder()
})
}
Then you can execute this whenever you want to unfocus the text field.
NotificationCenter.default.post(name: Notification.Name("UnfocusTextField"), object: nil)
But this will probably not work if the text field is currently active.